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 indivdual 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(sleep_time
=1):
794 # 'networkctl reload' asynchronously reconfigure links.
795 # Hence, we need to wait for a short time for link to be in configuring state.
797 time
.sleep(sleep_time
)
799 def resolvectl(*args
):
800 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
802 def timedatectl(*args
):
803 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
808 def tear_down_common():
809 # 1. stop DHCP/RA servers
815 call_quiet('rmmod netdevsim')
816 call_quiet('rmmod sch_teql')
818 # 3. remove network namespace
819 call_quiet('ip netns del ns99')
829 clear_network_units()
830 clear_networkd_conf_dropins()
835 flush_routing_policy_rules()
839 rm_rf(networkd_ci_temp_dir
)
840 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
842 clear_network_units()
843 clear_networkd_conf_dropins()
846 setup_systemd_udev_rules()
847 copy_udev_rule('00-debug-net.rules')
851 save_existing_links()
853 save_routing_policy_rules()
856 create_service_dropin('systemd-networkd', networkd_bin
,
859 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
861 'StartLimitIntervalSec=0'])
862 create_service_dropin('systemd-resolved', resolved_bin
)
863 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
865 # TODO: also run udevd with sanitizers, valgrind, or coverage
866 #create_service_dropin('systemd-udevd', udevd_bin,
867 # f'{udevadm_bin} control --reload --timeout 0')
869 'systemd-udevd.service',
873 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
877 'systemd-networkd.socket',
880 'StartLimitIntervalSec=0',
884 check_output('systemctl daemon-reload')
885 print(check_output('systemctl cat systemd-networkd.service'))
886 print(check_output('systemctl cat systemd-resolved.service'))
887 print(check_output('systemctl cat systemd-timesyncd.service'))
888 print(check_output('systemctl cat systemd-udevd.service'))
889 check_output('systemctl restart systemd-resolved.service')
890 check_output('systemctl restart systemd-timesyncd.service')
891 check_output('systemctl restart systemd-udevd.service')
893 def tearDownModule():
894 rm_rf(networkd_ci_temp_dir
)
896 clear_network_units()
897 clear_networkd_conf_dropins()
901 rm_rf('/run/systemd/system/systemd-networkd.service.d')
902 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
903 rm_rf('/run/systemd/system/systemd-resolved.service.d')
904 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
905 rm_rf('/run/systemd/system/systemd-udevd.service.d')
906 check_output('systemctl daemon-reload')
907 check_output('systemctl restart systemd-udevd.service')
908 restore_active_units()
911 # pylint: disable=no-member
913 def check_link_exists(self
, link
, expected
=True):
915 self
.assertTrue(link_exists(link
))
917 self
.assertFalse(link_exists(link
))
919 def check_link_attr(self
, *args
):
920 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
922 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
923 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
924 if allow_enoent
and not os
.path
.exists(path
):
926 with
open(path
, encoding
='utf-8') as f
:
927 self
.assertEqual(f
.readline().strip(), expected
)
929 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
930 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
932 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
933 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
935 def check_ipv6_neigh_sysctl_attr(self
, link
, attribute
, expected
):
936 self
.assertEqual(read_ipv6_neigh_sysctl_attr(link
, attribute
), expected
)
938 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
939 def links_exist(*links
):
941 if not link_exists(link
):
945 for iteration
in range(timeout
+ 1):
949 if links_exist(*links
):
952 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
955 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
956 # wait for the interface is activated.
957 needle
= f
'{link}: Bringing link {state}'
959 for iteration
in range(timeout
+ 1):
962 if not link_exists(link
):
964 output
= read_networkd_log()
965 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
968 self
.fail(f
'Timed out waiting for {link} activated.')
971 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
972 """Wait for the link to reach the specified operstate and/or setup state.
974 Specify None or '' for either operstate or setup_state to ignore that state.
975 This will recheck until the state conditions are met or the timeout expires.
977 If the link successfully matches the requested state, this returns True.
978 If this times out waiting for the link to match, the behavior depends on the
979 'fail_assert' parameter; if True, this causes a test assertion failure,
980 otherwise this returns False. The default is to cause assertion failure.
982 Note that this function matches on *exactly* the given operstate and setup_state.
983 To wait for a link to reach *or exceed* a given operstate, use wait_online().
990 for secs
in range(setup_timeout
+ 1):
993 if not link_exists(link
):
995 output
= networkctl_status(link
)
996 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
1000 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1003 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
1004 """Wait for the links to reach the specified operstate and/or setup state.
1006 This is similar to wait_operstate() but can be used for multiple links,
1007 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1008 The operstate should be specified in the link name, like 'eth0:degraded'.
1009 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1011 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1012 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1014 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1015 If this is set, no setup_state checks are done.
1017 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1018 This is applied only for the operational state 'degraded' or above.
1020 Note that this function waits for the links to reach *or exceed* the given operstate.
1021 However, the setup_state, if specified, must be matched *exactly*.
1023 This returns if the links reached the requested operstate/setup_state; otherwise it
1024 raises CalledProcessError or fails test assertion.
1026 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1034 check_output(*args
, env
=wait_online_env
)
1035 except subprocess
.CalledProcessError
:
1036 if networkd_is_failed():
1037 print('!!!!! systemd-networkd.service is failed !!!!!')
1038 call('systemctl status systemd-networkd.service')
1040 # show detailed status on failure
1041 for link
in links_with_operstate
:
1042 name
= link
.split(':')[0]
1043 if link_exists(name
):
1044 networkctl_status(name
)
1046 if not bool_any
and setup_state
:
1047 for link
in links_with_operstate
:
1048 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1050 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1051 for i
in range(timeout_sec
):
1054 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1055 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1058 self
.assertRegex(output
, address_regex
)
1060 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1061 for i
in range(timeout_sec
):
1064 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1065 if not re
.search(address_regex
, output
):
1068 self
.assertNotRegex(output
, address_regex
)
1070 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1071 for i
in range(timeout_sec
):
1074 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1075 if re
.search(route_regex
, output
):
1078 self
.assertRegex(output
, route_regex
)
1080 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1081 if not shutil
.which('selinuxenabled'):
1082 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1083 elif call_quiet('selinuxenabled') != 0:
1084 print('## Checking NetLabel skipped: SELinux disabled.')
1085 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1086 print('## Checking NetLabel skipped: netlabelctl command not found.')
1088 output
= check_output('netlabelctl unlbl list')
1090 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1092 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1093 if not shutil
.which('nft'):
1094 print('## Setting up NFT sets skipped: nft command not found.')
1096 if call(f
'nft add table inet sd_test') != 0:
1097 print('## Setting up NFT table failed.')
1099 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1100 print('## Setting up NFT sets failed.')
1103 def teardown_nftset(self
, *filters
):
1104 if not shutil
.which('nft'):
1105 print('## Tearing down NFT sets skipped: nft command not found.')
1107 for filter_name
in filters
:
1108 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1109 print('## Tearing down NFT sets failed.')
1111 if call(f
'nft delete table inet sd_test') != 0:
1112 print('## Tearing down NFT table failed.')
1115 def check_nftset(self
, filter_name
, contents
):
1116 if not shutil
.which('nft'):
1117 print('## Checking NFT sets skipped: nft command not found.')
1119 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1121 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1123 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1131 @expectedFailureIfAlternativeNameIsNotAvailable()
1132 def test_altname(self
):
1133 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1135 self
.wait_online(['dummy98:degraded'])
1137 output
= networkctl_status('dummy98')
1138 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1140 @expectedFailureIfAlternativeNameIsNotAvailable()
1141 def test_rename_to_altname(self
):
1142 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1143 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1145 self
.wait_online(['dummyalt:degraded'])
1147 output
= networkctl_status('dummyalt')
1148 self
.assertIn('hogehogehogehogehogehoge', output
)
1149 self
.assertNotIn('dummy98', output
)
1151 def test_reconfigure(self
):
1152 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1154 self
.wait_online(['dummy98:routable'])
1156 output
= check_output('ip -4 address show dev dummy98')
1158 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1159 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1160 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1162 check_output('ip address del 10.1.2.3/16 dev dummy98')
1163 check_output('ip address del 10.1.2.4/16 dev dummy98')
1164 check_output('ip address del 10.2.2.4/16 dev dummy98')
1166 networkctl_reconfigure('dummy98')
1167 self
.wait_online(['dummy98:routable'])
1169 output
= check_output('ip -4 address show dev dummy98')
1171 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1172 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1173 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1175 remove_network_unit('25-address-static.network')
1178 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1180 output
= check_output('ip -4 address show dev dummy98')
1182 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1183 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1184 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1186 copy_network_unit('25-address-static.network', copy_dropins
=False)
1188 self
.wait_online(['dummy98:routable'])
1190 output
= check_output('ip -4 address show dev dummy98')
1192 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1193 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1194 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1196 def test_renew(self
):
1198 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
1199 output
= networkctl_status('veth99')
1201 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1202 self
.assertIn('Gateway: 192.168.5.3', output
)
1203 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1204 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1206 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1209 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1211 for verb
in ['renew', 'forcerenew']:
1212 networkctl(verb
, 'veth99')
1214 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1217 def test_up_down(self
):
1218 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1220 self
.wait_online(['dummy98:routable'])
1222 networkctl('down', 'dummy98')
1223 self
.wait_online(['dummy98:off'])
1224 networkctl('up', 'dummy98')
1225 self
.wait_online(['dummy98:routable'])
1226 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1227 self
.wait_online(['dummy98:off'])
1228 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1229 self
.wait_online(['dummy98:routable'])
1231 def test_reload(self
):
1234 copy_network_unit('11-dummy.netdev')
1236 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1238 copy_network_unit('11-dummy.network')
1240 self
.wait_online(['test1:degraded'])
1242 remove_network_unit('11-dummy.network')
1244 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1246 remove_network_unit('11-dummy.netdev')
1248 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1250 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1252 self
.wait_operstate('test1', 'degraded')
1254 def test_glob(self
):
1255 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1258 self
.wait_online(['test1:degraded'])
1260 output
= networkctl('list')
1261 self
.assertRegex(output
, '1 lo ')
1262 self
.assertRegex(output
, 'test1')
1264 output
= networkctl('list', 'test1')
1265 self
.assertNotRegex(output
, '1 lo ')
1266 self
.assertRegex(output
, 'test1')
1268 output
= networkctl('list', 'te*')
1269 self
.assertNotRegex(output
, '1 lo ')
1270 self
.assertRegex(output
, 'test1')
1272 output
= networkctl_status('te*')
1273 self
.assertNotRegex(output
, '1: lo ')
1274 self
.assertRegex(output
, 'test1')
1276 output
= networkctl_status('tes[a-z][0-9]')
1277 self
.assertNotRegex(output
, '1: lo ')
1278 self
.assertRegex(output
, 'test1')
1281 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1284 self
.wait_online(['test1:degraded'])
1286 output
= networkctl_status('test1')
1287 self
.assertRegex(output
, 'MTU: 1600')
1289 def test_type(self
):
1290 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1292 self
.wait_online(['test1:degraded'])
1294 output
= networkctl_status('test1')
1296 self
.assertRegex(output
, 'Type: ether')
1298 output
= networkctl_status('lo')
1300 self
.assertRegex(output
, 'Type: loopback')
1302 def test_unit_file(self
):
1303 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1305 self
.wait_online(['test1:degraded'])
1307 output
= networkctl_status('test1')
1309 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1310 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1311 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1312 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1314 output
= read_networkd_log()
1315 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
)
1317 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1318 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1319 # Let's reprocess the interface and drop the property.
1320 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1321 output
= networkctl_status('lo')
1323 self
.assertIn('Link File: n/a', output
)
1324 self
.assertIn('Network File: n/a', output
)
1326 def test_delete_links(self
):
1327 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1328 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1331 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
1333 networkctl('delete', 'test1', 'veth99')
1334 self
.check_link_exists('test1', expected
=False)
1335 self
.check_link_exists('veth99', expected
=False)
1336 self
.check_link_exists('veth-peer', expected
=False)
1338 def test_label(self
):
1341 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1349 @expectedFailureIfAlternativeNameIsNotAvailable()
1350 def test_match(self
):
1351 copy_network_unit('12-dummy-mac.netdev',
1352 '12-dummy-match-mac-01.network',
1353 '12-dummy-match-mac-02.network',
1354 '12-dummy-match-renamed.network',
1355 '12-dummy-match-altname.network',
1356 '12-dummy-altname.link')
1359 self
.wait_online(['dummy98:routable'])
1360 output
= networkctl_status('dummy98')
1361 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1362 output
= check_output('ip -4 address show dev dummy98')
1363 self
.assertIn('10.0.0.1/16', output
)
1365 check_output('ip link set dev dummy98 down')
1366 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1368 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1369 self
.wait_online(['dummy98:routable'])
1370 output
= networkctl_status('dummy98')
1371 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1373 check_output('ip link set dev dummy98 down')
1374 check_output('ip link set dev dummy98 name dummy98-1')
1376 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1377 self
.wait_online(['dummy98-1:routable'])
1378 output
= networkctl_status('dummy98-1')
1379 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1381 check_output('ip link set dev dummy98-1 down')
1382 check_output('ip link set dev dummy98-1 name dummy98-2')
1383 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1385 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1386 self
.wait_online(['dummy98-2:routable'])
1387 output
= networkctl_status('dummy98-2')
1388 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1390 def test_match_udev_property(self
):
1391 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1393 self
.wait_online(['dummy98:routable'])
1395 output
= networkctl_status('dummy98')
1397 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1399 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1407 def test_wait_online_any(self
):
1408 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1411 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
1413 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1414 self
.wait_operstate('test1', 'degraded')
1416 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1424 def test_dropin_and_name_conflict(self
):
1425 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1428 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
1430 output
= check_output('ip link show dropin-test')
1432 self
.assertRegex(output
, '00:50:56:c0:00:28')
1434 @expectedFailureIfModuleIsNotAvailable('bareudp')
1435 def test_bareudp(self
):
1436 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1439 self
.wait_online(['bareudp99:degraded'])
1441 output
= check_output('ip -d link show bareudp99')
1443 self
.assertRegex(output
, 'dstport 1000 ')
1444 self
.assertRegex(output
, 'ethertype ip ')
1446 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1447 def test_batadv(self
):
1448 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1451 self
.wait_online(['batadv99:degraded'])
1453 output
= check_output('ip -d link show batadv99')
1455 self
.assertRegex(output
, 'batadv')
1457 def test_bridge(self
):
1458 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1461 self
.wait_online(['bridge99:no-carrier'])
1463 tick
= os
.sysconf('SC_CLK_TCK')
1464 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1465 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1466 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1467 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1468 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1469 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1470 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1471 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1472 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1474 output
= networkctl_status('bridge99')
1476 self
.assertRegex(output
, 'Priority: 9')
1477 self
.assertRegex(output
, 'STP: yes')
1478 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1480 output
= check_output('ip -d link show bridge99')
1482 self
.assertIn('vlan_filtering 1 ', output
)
1483 self
.assertIn('vlan_protocol 802.1ad ', output
)
1484 self
.assertIn('vlan_default_pvid 9 ', output
)
1486 def test_bond(self
):
1487 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1490 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1492 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1493 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1494 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1495 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1496 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1497 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1498 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1499 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1500 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1501 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1502 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1504 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1505 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1507 output
= networkctl_status('bond99')
1509 self
.assertIn('Mode: 802.3ad', output
)
1510 self
.assertIn('Miimon: 1s', output
)
1511 self
.assertIn('Updelay: 2s', output
)
1512 self
.assertIn('Downdelay: 2s', output
)
1514 output
= networkctl_status('bond98')
1516 self
.assertIn('Mode: balance-tlb', output
)
1518 def test_vlan(self
):
1519 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1520 '21-vlan.network', '21-vlan-test1.network')
1523 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1525 output
= check_output('ip -d link show test1')
1527 self
.assertRegex(output
, ' mtu 2000 ')
1529 output
= check_output('ip -d link show vlan99')
1531 self
.assertIn(' mtu 2000 ', output
)
1532 self
.assertIn('REORDER_HDR', output
)
1533 self
.assertIn('LOOSE_BINDING', output
)
1534 self
.assertIn('GVRP', output
)
1535 self
.assertIn('MVRP', output
)
1536 self
.assertIn(' id 99 ', output
)
1537 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1538 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1540 output
= check_output('ip -4 address show dev test1')
1542 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1543 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1545 output
= check_output('ip -4 address show dev vlan99')
1547 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1549 def test_vlan_on_bond(self
):
1550 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1551 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1553 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1554 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1556 self
.wait_online(['bond99:off'])
1557 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1559 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1560 # that the issue is fixed by the commit, let's allow to match both string.
1561 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1565 if log_re
.search(read_networkd_log()):
1570 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1572 self
.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
1574 def test_macvtap(self
):
1576 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1582 print(f
'### test_macvtap(mode={mode})')
1583 with self
.subTest(mode
=mode
):
1584 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1585 '11-dummy.netdev', '25-macvtap.network')
1586 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1587 f
.write('[MACVTAP]\nMode=' + mode
)
1590 self
.wait_online(['macvtap99:degraded',
1591 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1593 output
= check_output('ip -d link show macvtap99')
1595 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1597 def test_macvlan(self
):
1599 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1605 print(f
'### test_macvlan(mode={mode})')
1606 with self
.subTest(mode
=mode
):
1607 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1608 '11-dummy.netdev', '25-macvlan.network')
1609 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1610 f
.write('[MACVLAN]\nMode=' + mode
)
1613 self
.wait_online(['macvlan99:degraded',
1614 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1616 output
= check_output('ip -d link show test1')
1618 self
.assertRegex(output
, ' mtu 2000 ')
1620 output
= check_output('ip -d link show macvlan99')
1622 self
.assertRegex(output
, ' mtu 2000 ')
1623 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1625 remove_link('test1')
1628 check_output("ip link add test1 type dummy")
1629 self
.wait_online(['macvlan99:degraded',
1630 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1632 output
= check_output('ip -d link show test1')
1634 self
.assertRegex(output
, ' mtu 2000 ')
1636 output
= check_output('ip -d link show macvlan99')
1638 self
.assertRegex(output
, ' mtu 2000 ')
1639 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1641 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1642 def test_ipvlan(self
):
1644 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1650 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1651 with self
.subTest(mode
=mode
, flag
=flag
):
1652 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1653 '11-dummy.netdev', '25-ipvlan.network')
1654 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1655 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1658 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1660 output
= check_output('ip -d link show ipvlan99')
1662 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1664 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1665 def test_ipvtap(self
):
1667 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1673 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1674 with self
.subTest(mode
=mode
, flag
=flag
):
1675 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1676 '11-dummy.netdev', '25-ipvtap.network')
1677 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1678 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1681 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1683 output
= check_output('ip -d link show ipvtap99')
1685 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1687 def test_veth(self
):
1688 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1689 '25-veth-mtu.netdev')
1692 self
.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1694 output
= check_output('ip -d link show veth99')
1696 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1697 output
= check_output('ip -d link show veth-peer')
1699 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1701 output
= check_output('ip -d link show veth-mtu')
1703 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1704 self
.assertRegex(output
, 'mtu 1800')
1705 output
= check_output('ip -d link show veth-mtu-peer')
1707 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1708 self
.assertRegex(output
, 'mtu 1800')
1710 def test_tuntap(self
):
1711 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1714 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'])
1716 pid
= networkd_pid()
1717 name
= psutil
.Process(pid
).name()[:15]
1719 output
= check_output('ip -d tuntap show')
1721 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1722 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1724 output
= check_output('ip -d link show testtun99')
1726 # Old ip command does not support IFF_ flags
1727 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1728 self
.assertIn('UP,LOWER_UP', output
)
1730 output
= check_output('ip -d link show testtap99')
1732 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1733 self
.assertIn('UP,LOWER_UP', output
)
1735 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1738 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state
='unmanaged')
1740 pid
= networkd_pid()
1741 name
= psutil
.Process(pid
).name()[:15]
1743 output
= check_output('ip -d tuntap show')
1745 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1746 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1748 output
= check_output('ip -d link show testtun99')
1750 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1751 self
.assertIn('UP,LOWER_UP', output
)
1753 output
= check_output('ip -d link show testtap99')
1755 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1756 self
.assertIn('UP,LOWER_UP', output
)
1758 clear_network_units()
1760 self
.wait_online(['testtun99:off', 'testtap99:off'], setup_state
='unmanaged')
1762 output
= check_output('ip -d tuntap show')
1764 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1765 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1770 output
= check_output('ip -d link show testtun99')
1772 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1773 if 'NO-CARRIER' in output
:
1781 output
= check_output('ip -d link show testtap99')
1783 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1784 if 'NO-CARRIER' in output
:
1789 @expectedFailureIfModuleIsNotAvailable('vrf')
1791 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1794 self
.wait_online(['vrf99:carrier'])
1796 @expectedFailureIfModuleIsNotAvailable('vcan')
1797 def test_vcan(self
):
1798 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1799 '25-vcan98.netdev', '25-vcan98.network')
1802 self
.wait_online(['vcan99:carrier', 'vcan98:carrier'])
1803 # For can devices, 'carrier' is the default required operational state.
1804 self
.wait_online(['vcan99', 'vcan98'])
1806 # https://github.com/systemd/systemd/issues/30140
1807 output
= check_output('ip -d link show vcan99')
1809 self
.assertIn('mtu 16 ', output
)
1811 output
= check_output('ip -d link show vcan98')
1813 self
.assertIn('mtu 16 ', output
)
1815 @expectedFailureIfModuleIsNotAvailable('vxcan')
1816 def test_vxcan(self
):
1817 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1820 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1821 # For can devices, 'carrier' is the default required operational state.
1822 self
.wait_online(['vxcan99', 'vxcan-peer'])
1824 @expectedFailureIfModuleIsNotAvailable('wireguard')
1825 def test_wireguard(self
):
1826 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1827 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1828 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1830 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1831 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1832 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1833 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1835 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1837 output
= check_output('ip -4 address show dev wg99')
1839 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1841 output
= check_output('ip -4 address show dev wg99')
1843 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1845 output
= check_output('ip -6 address show dev wg99')
1847 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1849 output
= check_output('ip -4 address show dev wg98')
1851 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1853 output
= check_output('ip -6 address show dev wg98')
1855 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1857 output
= check_output('ip -4 route show dev wg99 table 1234')
1859 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1861 output
= check_output('ip -6 route show dev wg99 table 1234')
1863 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1865 output
= check_output('ip -6 route show dev wg98 table 1234')
1867 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1868 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1869 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1870 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1871 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1872 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1873 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1909 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1910 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1911 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1912 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1914 if shutil
.which('wg'):
1917 output
= check_output('wg show wg99 listen-port')
1918 self
.assertEqual(output
, '51820')
1919 output
= check_output('wg show wg99 fwmark')
1920 self
.assertEqual(output
, '0x4d2')
1921 output
= check_output('wg show wg99 private-key')
1922 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1923 output
= check_output('wg show wg99 allowed-ips')
1924 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1925 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1926 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1927 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1928 output
= check_output('wg show wg99 persistent-keepalive')
1929 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1930 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1931 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1932 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1933 output
= check_output('wg show wg99 endpoints')
1934 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1935 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1936 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1937 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1938 output
= check_output('wg show wg99 preshared-keys')
1939 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1940 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1941 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1942 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1944 output
= check_output('wg show wg98 private-key')
1945 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1947 output
= check_output('wg show wg97 listen-port')
1948 self
.assertEqual(output
, '51821')
1949 output
= check_output('wg show wg97 fwmark')
1950 self
.assertEqual(output
, '0x4d3')
1952 def test_geneve(self
):
1953 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1956 self
.wait_online(['geneve99:degraded'])
1958 output
= check_output('ip -d link show geneve99')
1960 self
.assertRegex(output
, '192.168.22.1')
1961 self
.assertRegex(output
, '6082')
1962 self
.assertRegex(output
, 'udpcsum')
1963 self
.assertRegex(output
, 'udp6zerocsumrx')
1965 def test_ipip_tunnel(self
):
1966 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1967 '25-ipip-tunnel.netdev', '25-tunnel.network',
1968 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1969 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1970 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1972 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1974 output
= check_output('ip -d link show ipiptun99')
1976 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1977 output
= check_output('ip -d link show ipiptun98')
1979 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1980 output
= check_output('ip -d link show ipiptun97')
1982 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1983 output
= check_output('ip -d link show ipiptun96')
1985 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1987 def test_gre_tunnel(self
):
1988 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1989 '25-gre-tunnel.netdev', '25-tunnel.network',
1990 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1991 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1992 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1994 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1996 output
= check_output('ip -d link show gretun99')
1998 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1999 self
.assertRegex(output
, 'ikey 1.2.3.103')
2000 self
.assertRegex(output
, 'okey 1.2.4.103')
2001 self
.assertRegex(output
, 'iseq')
2002 self
.assertRegex(output
, 'oseq')
2003 output
= check_output('ip -d link show gretun98')
2005 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2006 self
.assertRegex(output
, 'ikey 0.0.0.104')
2007 self
.assertRegex(output
, 'okey 0.0.0.104')
2008 self
.assertNotRegex(output
, 'iseq')
2009 self
.assertNotRegex(output
, 'oseq')
2010 output
= check_output('ip -d link show gretun97')
2012 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2013 self
.assertRegex(output
, 'ikey 0.0.0.105')
2014 self
.assertRegex(output
, 'okey 0.0.0.105')
2015 self
.assertNotRegex(output
, 'iseq')
2016 self
.assertNotRegex(output
, 'oseq')
2017 output
= check_output('ip -d link show gretun96')
2019 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2020 self
.assertRegex(output
, 'ikey 0.0.0.106')
2021 self
.assertRegex(output
, 'okey 0.0.0.106')
2022 self
.assertNotRegex(output
, 'iseq')
2023 self
.assertNotRegex(output
, 'oseq')
2025 def test_ip6gre_tunnel(self
):
2026 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2027 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2028 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2029 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2030 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2033 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2035 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2037 output
= check_output('ip -d link show ip6gretun99')
2039 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2040 output
= check_output('ip -d link show ip6gretun98')
2042 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2043 output
= check_output('ip -d link show ip6gretun97')
2045 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2046 output
= check_output('ip -d link show ip6gretun96')
2048 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2050 def test_gretap_tunnel(self
):
2051 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2052 '25-gretap-tunnel.netdev', '25-tunnel.network',
2053 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2055 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
2057 output
= check_output('ip -d link show gretap99')
2059 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2060 self
.assertRegex(output
, 'ikey 0.0.0.106')
2061 self
.assertRegex(output
, 'okey 0.0.0.106')
2062 self
.assertRegex(output
, 'iseq')
2063 self
.assertRegex(output
, 'oseq')
2064 self
.assertIn('nopmtudisc', output
)
2065 self
.assertIn('ignore-df', output
)
2066 output
= check_output('ip -d link show gretap98')
2068 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2069 self
.assertRegex(output
, 'ikey 0.0.0.107')
2070 self
.assertRegex(output
, 'okey 0.0.0.107')
2071 self
.assertRegex(output
, 'iseq')
2072 self
.assertRegex(output
, 'oseq')
2074 def test_ip6gretap_tunnel(self
):
2075 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2076 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2077 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2079 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
2081 output
= check_output('ip -d link show ip6gretap99')
2083 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2084 output
= check_output('ip -d link show ip6gretap98')
2086 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2088 def test_vti_tunnel(self
):
2089 copy_network_unit('12-dummy.netdev', '25-vti.network',
2090 '25-vti-tunnel.netdev', '25-tunnel.network',
2091 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2092 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2093 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2095 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2097 output
= check_output('ip -d link show vtitun99')
2099 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2100 output
= check_output('ip -d link show vtitun98')
2102 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2103 output
= check_output('ip -d link show vtitun97')
2105 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2106 output
= check_output('ip -d link show vtitun96')
2108 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2110 def test_vti6_tunnel(self
):
2111 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2112 '25-vti6-tunnel.netdev', '25-tunnel.network',
2113 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2114 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2116 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2118 output
= check_output('ip -d link show vti6tun99')
2120 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2121 output
= check_output('ip -d link show vti6tun98')
2123 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2124 output
= check_output('ip -d link show vti6tun97')
2126 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2128 def test_ip6tnl_tunnel(self
):
2129 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2130 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2131 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2132 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2133 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2134 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2135 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2137 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2138 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2139 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2141 output
= check_output('ip -d link show ip6tnl99')
2143 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2144 output
= check_output('ip -d link show ip6tnl98')
2146 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2147 output
= check_output('ip -d link show ip6tnl97')
2149 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2150 output
= check_output('ip -d link show ip6tnl-external')
2152 self
.assertIn('ip6tnl-external@NONE:', output
)
2153 self
.assertIn('ip6tnl external ', output
)
2154 output
= check_output('ip -d link show ip6tnl-slaac')
2156 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2158 output
= check_output('ip -6 address show veth99')
2160 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2162 output
= check_output('ip -4 route show default')
2164 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2166 def test_sit_tunnel(self
):
2167 copy_network_unit('12-dummy.netdev', '25-sit.network',
2168 '25-sit-tunnel.netdev', '25-tunnel.network',
2169 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2170 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2171 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2173 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2175 output
= check_output('ip -d link show sittun99')
2177 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2178 output
= check_output('ip -d link show sittun98')
2180 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2181 output
= check_output('ip -d link show sittun97')
2183 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2184 output
= check_output('ip -d link show sittun96')
2186 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2188 def test_isatap_tunnel(self
):
2189 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2190 '25-isatap-tunnel.netdev', '25-tunnel.network')
2192 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2194 output
= check_output('ip -d link show isataptun99')
2196 self
.assertRegex(output
, "isatap ")
2198 def test_6rd_tunnel(self
):
2199 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2200 '25-6rd-tunnel.netdev', '25-tunnel.network')
2202 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2204 output
= check_output('ip -d link show sittun99')
2206 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2208 @expectedFailureIfERSPANv0IsNotSupported()
2209 def test_erspan_tunnel_v0(self
):
2210 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2211 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2212 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2214 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2216 output
= check_output('ip -d link show erspan99')
2218 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2219 self
.assertIn('erspan_ver 0', output
)
2220 self
.assertNotIn('erspan_index 123', output
)
2221 self
.assertNotIn('erspan_dir ingress', output
)
2222 self
.assertNotIn('erspan_hwid 1f', output
)
2223 self
.assertIn('ikey 0.0.0.101', output
)
2224 self
.assertIn('iseq', output
)
2225 self
.assertIn('nopmtudisc', output
)
2226 self
.assertIn('ignore-df', output
)
2227 output
= check_output('ip -d link show erspan98')
2229 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2230 self
.assertIn('erspan_ver 0', output
)
2231 self
.assertNotIn('erspan_index 124', output
)
2232 self
.assertNotIn('erspan_dir egress', output
)
2233 self
.assertNotIn('erspan_hwid 2f', output
)
2234 self
.assertIn('ikey 0.0.0.102', output
)
2235 self
.assertIn('iseq', output
)
2237 def test_erspan_tunnel_v1(self
):
2238 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2239 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2240 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2242 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2244 output
= check_output('ip -d link show erspan99')
2246 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2247 self
.assertIn('erspan_ver 1', output
)
2248 self
.assertIn('erspan_index 123', output
)
2249 self
.assertNotIn('erspan_dir ingress', output
)
2250 self
.assertNotIn('erspan_hwid 1f', output
)
2251 self
.assertIn('ikey 0.0.0.101', output
)
2252 self
.assertIn('okey 0.0.0.101', output
)
2253 self
.assertIn('iseq', output
)
2254 self
.assertIn('oseq', output
)
2255 output
= check_output('ip -d link show erspan98')
2257 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2258 self
.assertIn('erspan_ver 1', output
)
2259 self
.assertIn('erspan_index 124', output
)
2260 self
.assertNotIn('erspan_dir egress', output
)
2261 self
.assertNotIn('erspan_hwid 2f', output
)
2262 self
.assertIn('ikey 0.0.0.102', output
)
2263 self
.assertIn('okey 0.0.0.102', output
)
2264 self
.assertIn('iseq', output
)
2265 self
.assertIn('oseq', output
)
2267 @expectedFailureIfERSPANv2IsNotSupported()
2268 def test_erspan_tunnel_v2(self
):
2269 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2270 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2271 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2273 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2275 output
= check_output('ip -d link show erspan99')
2277 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2278 self
.assertIn('erspan_ver 2', output
)
2279 self
.assertNotIn('erspan_index 123', output
)
2280 self
.assertIn('erspan_dir ingress', output
)
2281 self
.assertIn('erspan_hwid 0x1f', output
)
2282 self
.assertIn('ikey 0.0.0.101', output
)
2283 self
.assertIn('okey 0.0.0.101', output
)
2284 self
.assertIn('iseq', output
)
2285 self
.assertIn('oseq', output
)
2286 output
= check_output('ip -d link show erspan98')
2288 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2289 self
.assertIn('erspan_ver 2', output
)
2290 self
.assertNotIn('erspan_index 124', output
)
2291 self
.assertIn('erspan_dir egress', output
)
2292 self
.assertIn('erspan_hwid 0x2f', output
)
2293 self
.assertIn('ikey 0.0.0.102', output
)
2294 self
.assertIn('okey 0.0.0.102', output
)
2295 self
.assertIn('iseq', output
)
2296 self
.assertIn('oseq', output
)
2298 def test_tunnel_independent(self
):
2299 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2302 self
.wait_online(['ipiptun99:carrier'])
2304 def test_tunnel_independent_loopback(self
):
2305 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2308 self
.wait_online(['ipiptun99:carrier'])
2310 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2311 def test_xfrm(self
):
2312 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2313 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2314 '26-netdev-link-local-addressing-yes.network')
2317 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2319 output
= check_output('ip -d link show dev xfrm98')
2321 self
.assertIn('xfrm98@dummy98:', output
)
2322 self
.assertIn('xfrm if_id 0x98 ', output
)
2324 output
= check_output('ip -d link show dev xfrm99')
2326 self
.assertIn('xfrm99@lo:', output
)
2327 self
.assertIn('xfrm if_id 0x99 ', output
)
2329 @expectedFailureIfModuleIsNotAvailable('fou')
2331 # The following redundant check is necessary for CentOS CI.
2332 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2333 self
.assertTrue(is_module_available('fou'))
2335 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2336 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2337 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2340 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2342 output
= check_output('ip fou show')
2344 self
.assertRegex(output
, 'port 55555 ipproto 4')
2345 self
.assertRegex(output
, 'port 55556 ipproto 47')
2347 output
= check_output('ip -d link show ipiptun96')
2349 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2350 output
= check_output('ip -d link show sittun96')
2352 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2353 output
= check_output('ip -d link show gretun96')
2355 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2356 output
= check_output('ip -d link show gretap96')
2358 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2360 def test_vxlan(self
):
2361 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2362 '25-vxlan.netdev', '25-vxlan.network',
2363 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2364 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2365 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2366 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2369 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2370 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2372 output
= check_output('ip -d -d link show vxlan99')
2374 self
.assertIn('999', output
)
2375 self
.assertIn('5555', output
)
2376 self
.assertIn('l2miss', output
)
2377 self
.assertIn('l3miss', output
)
2378 self
.assertIn('gbp', output
)
2379 # Since [0] some of the options use slightly different names and some
2380 # options with default values are shown only if the -d(etails) setting
2382 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2383 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2384 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2385 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2386 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2387 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2389 output
= check_output('bridge fdb show dev vxlan99')
2391 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2392 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2393 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2395 output
= networkctl_status('vxlan99')
2397 self
.assertIn('VNI: 999', output
)
2398 self
.assertIn('Destination Port: 5555', output
)
2399 self
.assertIn('Underlying Device: test1', output
)
2401 output
= check_output('bridge fdb show dev vxlan97')
2403 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2404 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2405 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2407 output
= check_output('ip -d link show vxlan-slaac')
2409 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2411 output
= check_output('ip -6 address show veth99')
2413 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2415 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2416 def test_macsec(self
):
2417 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2418 '26-macsec.network', '12-dummy.netdev')
2421 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2423 output
= check_output('ip -d link show macsec99')
2425 self
.assertRegex(output
, 'macsec99@dummy98')
2426 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2427 self
.assertRegex(output
, 'encrypt on')
2429 output
= check_output('ip macsec show macsec99')
2431 self
.assertRegex(output
, 'encrypt on')
2432 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2433 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2434 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2435 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2436 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2437 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2438 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2439 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2440 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2441 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2442 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2444 def test_nlmon(self
):
2445 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2448 self
.wait_online(['nlmon99:carrier'])
2450 @expectedFailureIfModuleIsNotAvailable('ifb')
2452 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2455 self
.wait_online(['ifb99:degraded'])
2457 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2465 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2466 def test_l2tp_udp(self
):
2467 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2468 '25-l2tp-udp.netdev', '25-l2tp.network')
2471 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2473 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2475 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2476 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2477 self
.assertRegex(output
, "Peer tunnel 11")
2478 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2479 self
.assertRegex(output
, "UDP checksum: enabled")
2481 output
= check_output('ip l2tp show session tid 10 session_id 15')
2483 self
.assertRegex(output
, "Session 15 in tunnel 10")
2484 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2485 self
.assertRegex(output
, "interface name: l2tp-ses1")
2487 output
= check_output('ip l2tp show session tid 10 session_id 17')
2489 self
.assertRegex(output
, "Session 17 in tunnel 10")
2490 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2491 self
.assertRegex(output
, "interface name: l2tp-ses2")
2493 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2494 def test_l2tp_ip(self
):
2495 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2496 '25-l2tp-ip.netdev', '25-l2tp.network')
2499 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2501 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2503 self
.assertRegex(output
, "Tunnel 10, encap IP")
2504 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2505 self
.assertRegex(output
, "Peer tunnel 12")
2507 output
= check_output('ip l2tp show session tid 10 session_id 25')
2509 self
.assertRegex(output
, "Session 25 in tunnel 10")
2510 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2511 self
.assertRegex(output
, "interface name: l2tp-ses3")
2513 output
= check_output('ip l2tp show session tid 10 session_id 27')
2515 self
.assertRegex(output
, "Session 27 in tunnel 10")
2516 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2517 self
.assertRegex(output
, "interface name: l2tp-ses4")
2519 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2527 def verify_address_static(
2557 output
= check_output('ip address show dev dummy98')
2561 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2562 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2563 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2564 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2565 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2566 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2569 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2570 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2571 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2574 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2575 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2576 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2579 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2580 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2581 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2582 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2583 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2584 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2587 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2588 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2591 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2592 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2593 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2594 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2597 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2598 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2600 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2602 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2604 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2606 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2609 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2610 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2611 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2612 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2615 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2616 prefix16
= ip4_null_16
[:-len('.0.1')]
2617 self
.assertTrue(ip4_null_24
.endswith('.1'))
2618 prefix24
= ip4_null_24
[:-len('.1')]
2619 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2620 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2621 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2622 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2625 self
.assertNotIn('10.4.4.1', output
)
2626 self
.assertNotIn('10.5.4.1', output
)
2627 self
.assertNotIn('10.5.5.1', output
)
2628 self
.assertNotIn('10.8.2.1', output
)
2629 self
.assertNotIn('10.9.3.1', output
)
2630 self
.assertNotIn('2001:db8:0:f101::2', output
)
2631 self
.assertNotIn('2001:db8:0:f103::4', output
)
2634 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2636 check_json(networkctl_json())
2638 def test_address_static(self
):
2639 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2640 self
.setup_nftset('addr4', 'ipv4_addr')
2641 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2642 self
.setup_nftset('ifindex', 'iface_index')
2645 self
.wait_online(['dummy98:routable'])
2649 output
= check_output('ip -4 --json address show dev dummy98')
2650 for i
in json
.loads(output
)[0]['addr_info']:
2651 if i
['label'] == 'subnet16':
2652 ip4_null_16
= i
['local']
2653 elif i
['label'] == 'subnet24':
2654 ip4_null_24
= i
['local']
2655 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2656 self
.assertTrue(ip4_null_24
.endswith('.1'))
2660 output
= check_output('ip -6 --json address show dev dummy98')
2661 for i
in json
.loads(output
)[0]['addr_info']:
2662 if i
['prefixlen'] == 73:
2663 ip6_null_73
= i
['local']
2664 elif i
['prefixlen'] == 74:
2665 ip6_null_74
= i
['local']
2666 self
.assertTrue(ip6_null_73
.endswith(':1'))
2667 self
.assertTrue(ip6_null_74
.endswith(':1'))
2669 self
.verify_address_static(
2674 broadcast2
=' brd 10.4.2.255',
2675 broadcast3
=' brd 10.4.3.63',
2676 peer1
=' peer 10.5.1.101/24',
2677 peer2
=' peer 10.5.2.101/24',
2678 peer3
='/24 brd 10.5.3.255',
2679 peer4
=' peer 2001:db8:0:f103::101/128',
2680 peer5
=' peer 2001:db8:0:f103::102/128',
2685 deprecated2
=' deprecated',
2687 deprecated4
=' deprecated',
2689 flag1
=' noprefixroute',
2691 flag3
=' noprefixroute',
2692 flag4
=' home mngtmpaddr',
2693 ip4_null_16
=ip4_null_16
,
2694 ip4_null_24
=ip4_null_24
,
2695 ip6_null_73
=ip6_null_73
,
2696 ip6_null_74
=ip6_null_74
,
2699 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2700 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2701 self
.check_nftset('ifindex', 'dummy98')
2703 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2705 copy_network_unit('25-address-static.network.d/10-override.conf')
2707 self
.wait_online(['dummy98:routable'])
2708 self
.verify_address_static(
2709 label1
='new-label1',
2711 label3
='new-label3',
2712 broadcast1
=' brd 10.4.1.255',
2714 broadcast3
=' brd 10.4.3.31',
2715 peer1
=' peer 10.5.1.102/24',
2716 peer2
='/24 brd 10.5.2.255',
2717 peer3
=' peer 10.5.3.102/24',
2718 peer4
=' peer 2001:db8:0:f103::201/128',
2720 peer6
=' peer 2001:db8:0:f103::203/128',
2723 deprecated1
=' deprecated',
2725 deprecated3
=' deprecated',
2729 flag2
=' noprefixroute',
2730 flag3
=' home mngtmpaddr',
2731 flag4
=' noprefixroute',
2732 ip4_null_16
=ip4_null_16
,
2733 ip4_null_24
=ip4_null_24
,
2734 ip6_null_73
=ip6_null_73
,
2735 ip6_null_74
=ip6_null_74
,
2738 networkctl_reconfigure('dummy98')
2739 self
.wait_online(['dummy98:routable'])
2740 self
.verify_address_static(
2741 label1
='new-label1',
2743 label3
='new-label3',
2744 broadcast1
=' brd 10.4.1.255',
2746 broadcast3
=' brd 10.4.3.31',
2747 peer1
=' peer 10.5.1.102/24',
2748 peer2
='/24 brd 10.5.2.255',
2749 peer3
=' peer 10.5.3.102/24',
2750 peer4
=' peer 2001:db8:0:f103::201/128',
2752 peer6
=' peer 2001:db8:0:f103::203/128',
2755 deprecated1
=' deprecated',
2757 deprecated3
=' deprecated',
2761 flag2
=' noprefixroute',
2762 flag3
=' home mngtmpaddr',
2763 flag4
=' noprefixroute',
2764 ip4_null_16
=ip4_null_16
,
2765 ip4_null_24
=ip4_null_24
,
2766 ip6_null_73
=ip6_null_73
,
2767 ip6_null_74
=ip6_null_74
,
2771 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2772 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2773 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2774 output
= check_output('ip address show dev dummy98')
2776 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2777 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2779 # 2. reconfigure the interface, and check the deprecated flag is set again
2780 networkctl_reconfigure('dummy98')
2781 self
.wait_online(['dummy98:routable'])
2782 self
.verify_address_static(
2783 label1
='new-label1',
2785 label3
='new-label3',
2786 broadcast1
=' brd 10.4.1.255',
2788 broadcast3
=' brd 10.4.3.31',
2789 peer1
=' peer 10.5.1.102/24',
2790 peer2
='/24 brd 10.5.2.255',
2791 peer3
=' peer 10.5.3.102/24',
2792 peer4
=' peer 2001:db8:0:f103::201/128',
2794 peer6
=' peer 2001:db8:0:f103::203/128',
2797 deprecated1
=' deprecated',
2799 deprecated3
=' deprecated',
2803 flag2
=' noprefixroute',
2804 flag3
=' home mngtmpaddr',
2805 flag4
=' noprefixroute',
2806 ip4_null_16
=ip4_null_16
,
2807 ip4_null_24
=ip4_null_24
,
2808 ip6_null_73
=ip6_null_73
,
2809 ip6_null_74
=ip6_null_74
,
2812 # test for ENOBUFS issue #17012 (with reload)
2813 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2815 self
.wait_online(['dummy98:routable'])
2816 output
= check_output('ip -4 address show dev dummy98')
2817 for i
in range(1, 254):
2818 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2820 # (with reconfigure)
2821 networkctl_reconfigure('dummy98')
2822 self
.wait_online(['dummy98:routable'])
2823 output
= check_output('ip -4 address show dev dummy98')
2824 for i
in range(1, 254):
2825 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2827 # test for an empty string assignment for Address= in [Network]
2828 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2830 self
.wait_online(['dummy98:routable'])
2831 output
= check_output('ip -4 address show dev dummy98')
2832 for i
in range(1, 254):
2833 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2834 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2836 def test_address_ipv4acd(self
):
2837 check_output('ip netns add ns99')
2838 check_output('ip link add veth99 type veth peer veth-peer')
2839 check_output('ip link set veth-peer netns ns99')
2840 check_output('ip link set veth99 up')
2841 check_output('ip netns exec ns99 ip link set veth-peer up')
2842 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2844 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2846 self
.wait_online(['veth99:routable'])
2848 output
= check_output('ip -4 address show dev veth99')
2850 self
.assertNotIn('192.168.100.10/24', output
)
2851 self
.assertIn('192.168.100.11/24', output
)
2853 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2855 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2857 output
= check_output('ip -4 address show dev veth99')
2859 self
.assertNotIn('192.168.100.10/24', output
)
2860 self
.assertIn('192.168.100.11/24', output
)
2862 def test_address_peer_ipv4(self
):
2863 # test for issue #17304
2864 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2866 for trial
in range(2):
2872 self
.wait_online(['dummy98:routable'])
2874 output
= check_output('ip -4 address show dev dummy98')
2875 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2877 @expectedFailureIfModuleIsNotAvailable('vrf')
2878 def test_prefix_route(self
):
2879 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2880 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2881 '25-vrf.netdev', '25-vrf.network')
2882 for trial
in range(2):
2888 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2890 output
= check_output('ip route show table 42 dev dummy98')
2891 print('### ip route show table 42 dev dummy98')
2893 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2894 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2895 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2896 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2897 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2898 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2899 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2900 output
= check_output('ip -6 route show table 42 dev dummy98')
2901 print('### ip -6 route show table 42 dev dummy98')
2905 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2906 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2907 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2908 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2909 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2910 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2911 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2912 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2916 output
= check_output('ip route show dev test1')
2917 print('### ip route show dev test1')
2919 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2920 output
= check_output('ip route show table local dev test1')
2921 print('### ip route show table local dev test1')
2923 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2924 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2925 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2926 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2927 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2928 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2929 output
= check_output('ip -6 route show dev test1')
2930 print('### ip -6 route show dev test1')
2932 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2933 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2934 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2935 output
= check_output('ip -6 route show table local dev test1')
2936 print('### ip -6 route show table local dev test1')
2938 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2939 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2940 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2941 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2942 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2944 def test_configure_without_carrier(self
):
2945 copy_network_unit('11-dummy.netdev')
2947 self
.wait_operstate('test1', 'off', '')
2948 check_output('ip link set dev test1 up carrier off')
2950 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2952 self
.wait_online(['test1:no-carrier'])
2954 carrier_map
= {'on': '1', 'off': '0'}
2955 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2956 for carrier
in ['off', 'on', 'off']:
2957 with self
.subTest(carrier
=carrier
):
2958 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2959 check_output(f
'ip link set dev test1 carrier {carrier}')
2960 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2962 output
= networkctl_status('test1')
2964 self
.assertRegex(output
, '192.168.0.15')
2965 self
.assertRegex(output
, '192.168.0.1')
2966 self
.assertRegex(output
, routable_map
[carrier
])
2968 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2969 copy_network_unit('11-dummy.netdev')
2971 self
.wait_operstate('test1', 'off', '')
2972 check_output('ip link set dev test1 up carrier off')
2974 copy_network_unit('25-test1.network')
2976 self
.wait_online(['test1:no-carrier'])
2978 carrier_map
= {'on': '1', 'off': '0'}
2979 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2980 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2981 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2982 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2983 check_output(f
'ip link set dev test1 carrier {carrier}')
2984 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2986 output
= networkctl_status('test1')
2989 self
.assertRegex(output
, '192.168.0.15')
2990 self
.assertRegex(output
, '192.168.0.1')
2992 self
.assertNotRegex(output
, '192.168.0.15')
2993 self
.assertNotRegex(output
, '192.168.0.1')
2994 self
.assertRegex(output
, routable_map
[carrier
])
2996 def test_routing_policy_rule(self
):
2997 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2999 self
.wait_online(['test1:degraded'])
3001 output
= check_output('ip rule list iif test1 priority 111')
3003 self
.assertRegex(output
, '111:')
3004 self
.assertRegex(output
, 'from 192.168.100.18')
3005 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3006 self
.assertRegex(output
, 'iif test1')
3007 self
.assertRegex(output
, 'oif test1')
3008 self
.assertRegex(output
, 'lookup 7')
3010 output
= check_output('ip rule list iif test1 priority 101')
3012 self
.assertRegex(output
, '101:')
3013 self
.assertRegex(output
, 'from all')
3014 self
.assertRegex(output
, 'iif test1')
3015 self
.assertRegex(output
, 'lookup 9')
3017 output
= check_output('ip -6 rule list iif test1 priority 100')
3019 self
.assertRegex(output
, '100:')
3020 self
.assertRegex(output
, 'from all')
3021 self
.assertRegex(output
, 'iif test1')
3022 self
.assertRegex(output
, 'lookup 8')
3024 output
= check_output('ip rule list iif test1 priority 102')
3026 self
.assertRegex(output
, '102:')
3027 self
.assertRegex(output
, 'from 0.0.0.0/8')
3028 self
.assertRegex(output
, 'iif test1')
3029 self
.assertRegex(output
, 'lookup 10')
3031 check_json(networkctl_json())
3033 def test_routing_policy_rule_issue_11280(self
):
3034 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3035 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3037 for trial
in range(3):
3038 restart_networkd(show_logs
=(trial
> 0))
3039 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
3041 output
= check_output('ip rule list table 7')
3043 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3045 output
= check_output('ip rule list table 8')
3047 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3049 def test_routing_policy_rule_reconfigure(self
):
3050 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3052 self
.wait_online(['test1:degraded'])
3054 output
= check_output('ip rule list table 1011')
3056 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3057 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3058 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3059 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3061 output
= check_output('ip -6 rule list table 1011')
3063 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3065 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3067 self
.wait_online(['test1:degraded'])
3069 output
= check_output('ip rule list table 1011')
3071 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3072 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3073 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3074 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3076 output
= check_output('ip -6 rule list table 1011')
3078 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3079 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3081 call('ip rule delete priority 10111')
3082 call('ip rule delete priority 10112')
3083 call('ip rule delete priority 10113')
3084 call('ip rule delete priority 10114')
3085 call('ip -6 rule delete priority 10113')
3087 output
= check_output('ip rule list table 1011')
3089 self
.assertEqual(output
, '')
3091 output
= check_output('ip -6 rule list table 1011')
3093 self
.assertEqual(output
, '')
3095 networkctl_reconfigure('test1')
3096 self
.wait_online(['test1:degraded'])
3098 output
= check_output('ip rule list table 1011')
3100 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3101 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3102 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3103 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3105 output
= check_output('ip -6 rule list table 1011')
3107 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3109 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3110 def test_routing_policy_rule_port_range(self
):
3111 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3113 self
.wait_online(['test1:degraded'])
3115 output
= check_output('ip rule')
3117 self
.assertRegex(output
, '111')
3118 self
.assertRegex(output
, 'from 192.168.100.18')
3119 self
.assertRegex(output
, '1123-1150')
3120 self
.assertRegex(output
, '3224-3290')
3121 self
.assertRegex(output
, 'tcp')
3122 self
.assertRegex(output
, 'lookup 7')
3124 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3125 def test_routing_policy_rule_invert(self
):
3126 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3128 self
.wait_online(['test1:degraded'])
3130 output
= check_output('ip rule')
3132 self
.assertRegex(output
, '111')
3133 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3134 self
.assertRegex(output
, 'tcp')
3135 self
.assertRegex(output
, 'lookup 7')
3137 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3138 def test_routing_policy_rule_l3mdev(self
):
3139 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3141 self
.wait_online(['test1:degraded'])
3143 output
= check_output('ip rule')
3145 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3146 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3148 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3149 def test_routing_policy_rule_uidrange(self
):
3150 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3152 self
.wait_online(['test1:degraded'])
3154 output
= check_output('ip rule')
3156 self
.assertRegex(output
, '111')
3157 self
.assertRegex(output
, 'from 192.168.100.18')
3158 self
.assertRegex(output
, 'lookup 7')
3159 self
.assertRegex(output
, 'uidrange 100-200')
3161 def _test_route_static(self
, manage_foreign_routes
):
3162 if not manage_foreign_routes
:
3163 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3165 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3166 '25-route-static-test1.network', '11-dummy.netdev')
3168 self
.wait_online(['dummy98:routable'])
3170 output
= networkctl_status('dummy98')
3173 print('### ip -6 route show dev dummy98')
3174 output
= check_output('ip -6 route show dev dummy98')
3176 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3177 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3178 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3180 print('### ip -6 route show default')
3181 output
= check_output('ip -6 route show default')
3183 self
.assertIn('default', output
)
3184 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3186 print('### ip -4 route show dev dummy98')
3187 output
= check_output('ip -4 route show dev dummy98')
3189 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3190 self
.assertIn('149.10.124.64 proto static scope link', output
)
3191 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3192 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3193 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3194 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3195 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3196 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3198 print('### ip -4 route show dev dummy98 default')
3199 output
= check_output('ip -4 route show dev dummy98 default')
3201 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3202 self
.assertIn('default via 149.10.124.64 proto static', output
)
3203 self
.assertIn('default proto static', output
)
3204 self
.assertIn('default via 1.1.8.104 proto static', output
)
3206 print('### ip -4 route show table local dev dummy98')
3207 output
= check_output('ip -4 route show table local dev dummy98')
3209 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3210 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3211 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3213 print('### ip -4 route show type blackhole')
3214 output
= check_output('ip -4 route show type blackhole')
3216 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3218 print('### ip -4 route show type unreachable')
3219 output
= check_output('ip -4 route show type unreachable')
3221 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3223 print('### ip -4 route show type prohibit')
3224 output
= check_output('ip -4 route show type prohibit')
3226 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3228 print('### ip -6 route show type blackhole')
3229 output
= check_output('ip -6 route show type blackhole')
3231 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3233 print('### ip -6 route show type unreachable')
3234 output
= check_output('ip -6 route show type unreachable')
3236 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3238 print('### ip -6 route show type prohibit')
3239 output
= check_output('ip -6 route show type prohibit')
3241 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3243 print('### ip route show 192.168.10.1')
3244 output
= check_output('ip route show 192.168.10.1')
3246 self
.assertIn('192.168.10.1 proto static', output
)
3247 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3248 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3249 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3250 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3252 print('### ip route show 192.168.10.2')
3253 output
= check_output('ip route show 192.168.10.2')
3255 # old ip command does not show IPv6 gateways...
3256 self
.assertIn('192.168.10.2 proto static', output
)
3257 self
.assertIn('nexthop', output
)
3258 self
.assertIn('dev test1 weight 20', output
)
3259 self
.assertIn('dev test1 weight 30', output
)
3260 self
.assertIn('dev dummy98 weight 10', output
)
3261 self
.assertIn('dev dummy98 weight 5', output
)
3263 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3264 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3266 # old ip command does not show 'nexthop' keyword and weight...
3267 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3268 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3269 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3270 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3271 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3273 check_json(networkctl_json())
3275 copy_network_unit('25-address-static.network', copy_dropins
=False)
3277 self
.wait_online(['dummy98:routable'])
3279 # check all routes managed by Manager are removed
3280 print('### ip -4 route show type blackhole')
3281 output
= check_output('ip -4 route show type blackhole')
3283 self
.assertEqual(output
, '')
3285 print('### ip -4 route show type unreachable')
3286 output
= check_output('ip -4 route show type unreachable')
3288 self
.assertEqual(output
, '')
3290 print('### ip -4 route show type prohibit')
3291 output
= check_output('ip -4 route show type prohibit')
3293 self
.assertEqual(output
, '')
3295 print('### ip -6 route show type blackhole')
3296 output
= check_output('ip -6 route show type blackhole')
3298 self
.assertEqual(output
, '')
3300 print('### ip -6 route show type unreachable')
3301 output
= check_output('ip -6 route show type unreachable')
3303 self
.assertEqual(output
, '')
3305 print('### ip -6 route show type prohibit')
3306 output
= check_output('ip -6 route show type prohibit')
3308 self
.assertEqual(output
, '')
3310 remove_network_unit('25-address-static.network')
3312 self
.wait_online(['dummy98:routable'])
3314 # check all routes managed by Manager are reconfigured
3315 print('### ip -4 route show type blackhole')
3316 output
= check_output('ip -4 route show type blackhole')
3318 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3320 print('### ip -4 route show type unreachable')
3321 output
= check_output('ip -4 route show type unreachable')
3323 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3325 print('### ip -4 route show type prohibit')
3326 output
= check_output('ip -4 route show type prohibit')
3328 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3330 print('### ip -6 route show type blackhole')
3331 output
= check_output('ip -6 route show type blackhole')
3333 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3335 print('### ip -6 route show type unreachable')
3336 output
= check_output('ip -6 route show type unreachable')
3338 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3340 print('### ip -6 route show type prohibit')
3341 output
= check_output('ip -6 route show type prohibit')
3343 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3345 remove_link('dummy98')
3348 # check all routes managed by Manager are removed
3349 print('### ip -4 route show type blackhole')
3350 output
= check_output('ip -4 route show type blackhole')
3352 self
.assertEqual(output
, '')
3354 print('### ip -4 route show type unreachable')
3355 output
= check_output('ip -4 route show type unreachable')
3357 self
.assertEqual(output
, '')
3359 print('### ip -4 route show type prohibit')
3360 output
= check_output('ip -4 route show type prohibit')
3362 self
.assertEqual(output
, '')
3364 print('### ip -6 route show type blackhole')
3365 output
= check_output('ip -6 route show type blackhole')
3367 self
.assertEqual(output
, '')
3369 print('### ip -6 route show type unreachable')
3370 output
= check_output('ip -6 route show type unreachable')
3372 self
.assertEqual(output
, '')
3374 print('### ip -6 route show type prohibit')
3375 output
= check_output('ip -6 route show type prohibit')
3377 self
.assertEqual(output
, '')
3379 def test_route_static(self
):
3381 for manage_foreign_routes
in [True, False]:
3387 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3388 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3389 self
._test
_route
_static
(manage_foreign_routes
)
3391 @expectedFailureIfRTA_VIAIsNotSupported()
3392 def test_route_via_ipv6(self
):
3393 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3395 self
.wait_online(['dummy98:routable'])
3397 output
= networkctl_status('dummy98')
3400 print('### ip -6 route show dev dummy98')
3401 output
= check_output('ip -6 route show dev dummy98')
3403 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3404 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3406 print('### ip -4 route show dev dummy98')
3407 output
= check_output('ip -4 route show dev dummy98')
3409 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3410 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3412 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3413 def test_route_congctl(self
):
3414 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3416 self
.wait_online(['dummy98:routable'])
3418 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3419 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3421 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3422 self
.assertIn('congctl dctcp', output
)
3424 print('### ip -4 route show dev dummy98 149.10.124.66')
3425 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3427 self
.assertIn('149.10.124.66 proto static', output
)
3428 self
.assertIn('congctl dctcp', output
)
3429 self
.assertIn('rto_min 300s', output
)
3431 @expectedFailureIfModuleIsNotAvailable('vrf')
3432 def test_route_vrf(self
):
3433 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3434 '25-vrf.netdev', '25-vrf.network')
3436 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3438 output
= check_output('ip route show vrf vrf99')
3440 self
.assertRegex(output
, 'default via 192.168.100.1')
3442 output
= check_output('ip route show')
3444 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3446 def test_gateway_reconfigure(self
):
3447 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3449 self
.wait_online(['dummy98:routable'])
3450 print('### ip -4 route show dev dummy98 default')
3451 output
= check_output('ip -4 route show dev dummy98 default')
3453 self
.assertIn('default via 149.10.124.59 proto static', output
)
3454 self
.assertNotIn('149.10.124.60', output
)
3456 remove_network_unit('25-gateway-static.network')
3457 copy_network_unit('25-gateway-next-static.network')
3459 self
.wait_online(['dummy98:routable'])
3460 print('### ip -4 route show dev dummy98 default')
3461 output
= check_output('ip -4 route show dev dummy98 default')
3463 self
.assertNotIn('149.10.124.59', output
)
3464 self
.assertIn('default via 149.10.124.60 proto static', output
)
3466 def test_ip_route_ipv6_src_route(self
):
3467 # a dummy device does not make the addresses go through tentative state, so we
3468 # reuse a bond from an earlier test, which does make the addresses go through
3469 # tentative state, and do our test on that
3470 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3472 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3474 output
= check_output('ip -6 route list dev bond199')
3476 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3478 def test_route_preferred_source_with_existing_address(self
):
3480 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3485 networkctl_reconfigure('dummy98')
3487 self
.wait_online(['dummy98:routable'])
3489 output
= check_output('ip -6 route list dev dummy98')
3491 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3493 def test_ip_link_mac_address(self
):
3494 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3496 self
.wait_online(['dummy98:degraded'])
3498 output
= check_output('ip link show dummy98')
3500 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3502 def test_ip_link_unmanaged(self
):
3503 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3506 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3508 def test_ipv6_address_label(self
):
3509 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3511 self
.wait_online(['dummy98:degraded'])
3513 output
= check_output('ip addrlabel list')
3515 self
.assertRegex(output
, '2004:da8:1::/64')
3517 def test_ipv6_proxy_ndp(self
):
3518 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3521 self
.wait_online(['dummy98:routable'])
3523 output
= check_output('ip neighbor show proxy dev dummy98')
3525 for i
in range(1, 5):
3526 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3528 def test_ipv6_neigh_retrans_time(self
):
3530 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3533 self
.wait_online([f
'{link}:degraded'])
3534 remove_network_unit('25-dummy.network')
3536 # expect retrans_time_ms updated
3537 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3539 self
.wait_online([f
'{link}:degraded'])
3540 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3541 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3543 # expect retrans_time_ms unchanged
3544 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3546 self
.wait_online([f
'{link}:degraded'])
3547 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3548 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3550 # expect retrans_time_ms unchanged
3551 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3553 self
.wait_online([f
'{link}:degraded'])
3554 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3555 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3557 # expect retrans_time_ms unchanged
3558 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3560 self
.wait_online([f
'{link}:degraded'])
3561 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3562 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3564 # expect retrans_time_ms unchanged
3565 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3567 self
.wait_online([f
'{link}:degraded'])
3568 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3569 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3571 # expect retrans_time_ms updated
3572 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3574 self
.wait_online([f
'{link}:degraded'])
3575 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3576 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3578 def test_neighbor(self
):
3579 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3580 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3581 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3584 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'])
3586 print('### ip neigh list dev gretun97')
3587 output
= check_output('ip neigh list dev gretun97')
3589 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3590 self
.assertNotIn('10.0.0.23', output
)
3592 print('### ip neigh list dev ip6gretun97')
3593 output
= check_output('ip neigh list dev ip6gretun97')
3595 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3596 self
.assertNotIn('2001:db8:0:f102::18', output
)
3598 print('### ip neigh list dev dummy98')
3599 output
= check_output('ip neigh list dev dummy98')
3601 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3602 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3603 self
.assertNotIn('2004:da8:1:0::2', output
)
3604 self
.assertNotIn('192.168.10.2', output
)
3605 self
.assertNotIn('00:00:5e:00:02:67', output
)
3607 check_json(networkctl_json())
3609 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3610 # the valid configurations in 10-step1.conf.
3611 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3613 self
.wait_online(['dummy98:degraded'])
3615 print('### ip neigh list dev dummy98')
3616 output
= check_output('ip neigh list dev dummy98')
3618 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3619 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3620 self
.assertNotIn('2004:da8:1:0::2', output
)
3621 self
.assertNotIn('192.168.10.2', output
)
3622 self
.assertNotIn('00:00:5e:00:02:67', output
)
3624 check_json(networkctl_json())
3626 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3627 '25-neighbor-dummy.network.d/10-step2.conf')
3628 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3630 self
.wait_online(['dummy98:degraded'])
3632 print('### ip neigh list dev dummy98')
3633 output
= check_output('ip neigh list dev dummy98')
3635 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3636 self
.assertNotIn('00:00:5e:00:02:65', output
)
3637 self
.assertNotIn('00:00:5e:00:02:66', output
)
3638 self
.assertNotIn('00:00:5e:00:03:65', output
)
3639 self
.assertNotIn('2004:da8:1::1', output
)
3641 def test_link_local_addressing(self
):
3642 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3643 '25-link-local-addressing-no.network', '12-dummy.netdev')
3645 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3647 output
= check_output('ip address show dev test1')
3649 self
.assertRegex(output
, 'inet .* scope link')
3650 self
.assertRegex(output
, 'inet6 .* scope link')
3652 output
= check_output('ip address show dev dummy98')
3654 self
.assertNotRegex(output
, 'inet6* .* scope link')
3656 # Documentation/networking/ip-sysctl.txt
3658 # addr_gen_mode - INTEGER
3659 # Defines how link-local and autoconf addresses are generated.
3661 # 0: generate address based on EUI64 (default)
3662 # 1: do no generate a link-local address, use EUI64 for addresses generated
3664 # 2: generate stable privacy addresses, using the secret from
3665 # stable_secret (RFC7217)
3666 # 3: generate stable privacy addresses, using a random secret if unset
3668 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3669 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3670 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3672 def test_link_local_addressing_ipv6ll(self
):
3673 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3675 self
.wait_online(['dummy98:degraded'])
3677 # An IPv6LL address exists by default.
3678 output
= check_output('ip address show dev dummy98')
3680 self
.assertRegex(output
, 'inet6 .* scope link')
3682 copy_network_unit('25-link-local-addressing-no.network')
3684 self
.wait_online(['dummy98:carrier'])
3686 # Check if the IPv6LL address is removed.
3687 output
= check_output('ip address show dev dummy98')
3689 self
.assertNotRegex(output
, 'inet6 .* scope link')
3691 remove_network_unit('25-link-local-addressing-no.network')
3693 self
.wait_online(['dummy98:degraded'])
3695 # Check if a new IPv6LL address is assigned.
3696 output
= check_output('ip address show dev dummy98')
3698 self
.assertRegex(output
, 'inet6 .* scope link')
3700 @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
3701 def test_sysctl(self
):
3702 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3703 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3705 self
.wait_online(['dummy98:degraded'])
3707 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3708 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3709 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3710 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3711 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3712 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3713 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3714 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3715 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3716 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3718 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3720 self
.wait_online(['dummy98:degraded'])
3722 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3724 def test_sysctl_disable_ipv6(self
):
3725 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3727 print('## Disable ipv6')
3728 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3729 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3732 self
.wait_online(['dummy98:routable'])
3734 output
= check_output('ip -4 address show dummy98')
3736 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3737 output
= check_output('ip -6 address show dummy98')
3739 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3740 self
.assertRegex(output
, 'inet6 .* scope link')
3741 output
= check_output('ip -4 route show dev dummy98')
3743 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3744 output
= check_output('ip -6 route show default')
3746 self
.assertRegex(output
, 'default')
3747 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3749 remove_link('dummy98')
3751 print('## Enable ipv6')
3752 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3753 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3756 self
.wait_online(['dummy98:routable'])
3758 output
= check_output('ip -4 address show dummy98')
3760 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3761 output
= check_output('ip -6 address show dummy98')
3763 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3764 self
.assertRegex(output
, 'inet6 .* scope link')
3765 output
= check_output('ip -4 route show dev dummy98')
3767 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3768 output
= check_output('ip -6 route show default')
3770 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3772 def test_bind_carrier(self
):
3773 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3776 # no bound interface.
3777 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3778 output
= check_output('ip address show test1')
3780 self
.assertNotIn('UP,LOWER_UP', output
)
3781 self
.assertIn('DOWN', output
)
3782 self
.assertNotIn('192.168.10', output
)
3784 # add one bound interface. The interface will be up.
3785 check_output('ip link add dummy98 type dummy')
3786 check_output('ip link set dummy98 up')
3787 self
.wait_online(['test1:routable'])
3788 output
= check_output('ip address show test1')
3790 self
.assertIn('UP,LOWER_UP', output
)
3791 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3793 # add another bound interface. The interface is still up.
3794 check_output('ip link add dummy99 type dummy')
3795 check_output('ip link set dummy99 up')
3796 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3797 output
= check_output('ip address show test1')
3799 self
.assertIn('UP,LOWER_UP', output
)
3800 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3802 # remove one of the bound interfaces. The interface is still up
3803 remove_link('dummy98')
3804 output
= check_output('ip address show test1')
3806 self
.assertIn('UP,LOWER_UP', output
)
3807 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3809 # bring down the remaining bound interface. The interface will be down.
3810 check_output('ip link set dummy99 down')
3811 self
.wait_operstate('test1', 'off')
3812 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3813 output
= check_output('ip address show test1')
3815 self
.assertNotIn('UP,LOWER_UP', output
)
3816 self
.assertIn('DOWN', output
)
3817 self
.assertNotIn('192.168.10', output
)
3819 # bring up the bound interface. The interface will be up.
3820 check_output('ip link set dummy99 up')
3821 self
.wait_online(['test1:routable'])
3822 output
= check_output('ip address show test1')
3824 self
.assertIn('UP,LOWER_UP', output
)
3825 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3827 # remove the remaining bound interface. The interface will be down.
3828 remove_link('dummy99')
3829 self
.wait_operstate('test1', 'off')
3830 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3831 output
= check_output('ip address show test1')
3833 self
.assertNotIn('UP,LOWER_UP', output
)
3834 self
.assertIn('DOWN', output
)
3835 self
.assertNotIn('192.168.10', output
)
3837 # re-add one bound interface. The interface will be up.
3838 check_output('ip link add dummy98 type dummy')
3839 check_output('ip link set dummy98 up')
3840 self
.wait_online(['test1:routable'])
3841 output
= check_output('ip address show test1')
3843 self
.assertIn('UP,LOWER_UP', output
)
3844 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3846 def _test_activation_policy(self
, interface
, test
):
3847 conffile
= '25-activation-policy.network'
3849 conffile
= f
'{conffile}.d/{test}.conf'
3850 if interface
== 'vlan99':
3851 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3852 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3855 always
= test
.startswith('always')
3856 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3857 expect_up
= initial_up
3858 next_up
= not expect_up
3860 if test
.endswith('down'):
3861 self
.wait_activated(interface
)
3863 for iteration
in range(4):
3864 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3865 operstate
= 'routable' if expect_up
else 'off'
3866 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3867 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3870 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3871 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3872 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3874 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3877 check_output(f
'ip link set dev {interface} up')
3879 check_output(f
'ip link set dev {interface} down')
3880 expect_up
= initial_up
if always
else next_up
3881 next_up
= not next_up
3885 def test_activation_policy(self
):
3887 for interface
in ['test1', 'vlan99']:
3888 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3894 print(f
'### test_activation_policy(interface={interface}, test={test})')
3895 with self
.subTest(interface
=interface
, test
=test
):
3896 self
._test
_activation
_policy
(interface
, test
)
3898 def _test_activation_policy_required_for_online(self
, policy
, required
):
3899 conffile
= '25-activation-policy.network'
3900 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3902 units
+= [f
'{conffile}.d/{policy}.conf']
3904 units
+= [f
'{conffile}.d/required-{required}.conf']
3905 copy_network_unit(*units
, copy_dropins
=False)
3908 if policy
.endswith('down'):
3909 self
.wait_activated('test1')
3911 if policy
.endswith('down') or policy
== 'manual':
3912 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3914 self
.wait_online(['test1'])
3916 if policy
== 'always-down':
3917 # if always-down, required for online is forced to no
3920 # otherwise if required for online is specified, it should match that
3921 expected
= required
== 'yes'
3923 # otherwise if only policy specified, required for online defaults to
3924 # true if policy is up, always-up, or bound
3925 expected
= policy
.endswith('up') or policy
== 'bound'
3927 # default is true, if neither are specified
3930 output
= networkctl_status('test1')
3933 yesno
= 'yes' if expected
else 'no'
3934 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3936 def test_activation_policy_required_for_online(self
):
3938 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3939 for required
in ['yes', 'no', '']:
3945 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3946 with self
.subTest(policy
=policy
, required
=required
):
3947 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3949 def test_domain(self
):
3950 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3952 self
.wait_online(['dummy98:routable'])
3954 output
= networkctl_status('dummy98')
3956 self
.assertRegex(output
, 'Address: 192.168.42.100')
3957 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3958 self
.assertRegex(output
, 'Search Domains: one')
3960 def test_keep_configuration_static(self
):
3961 check_output('ip link add name dummy98 type dummy')
3962 check_output('ip address add 10.1.2.3/16 dev dummy98')
3963 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3964 output
= check_output('ip address show dummy98')
3966 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3967 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3968 output
= check_output('ip route show dev dummy98')
3971 copy_network_unit('24-keep-configuration-static.network')
3973 self
.wait_online(['dummy98:routable'])
3975 output
= check_output('ip address show dummy98')
3977 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3978 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3980 def check_nexthop(self
, manage_foreign_nexthops
, first
):
3981 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3983 output
= check_output('ip nexthop list dev veth99')
3986 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3987 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3989 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
3990 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
3991 self
.assertIn('id 3 dev veth99', output
)
3992 self
.assertIn('id 4 dev veth99', output
)
3994 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3996 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
3997 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
3998 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3999 if manage_foreign_nexthops
:
4000 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
4002 output
= check_output('ip nexthop list dev dummy98')
4005 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4007 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4008 if manage_foreign_nexthops
:
4009 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4011 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4013 # kernel manages blackhole nexthops on lo
4014 output
= check_output('ip nexthop list dev lo')
4017 self
.assertIn('id 6 blackhole', output
)
4018 self
.assertIn('id 7 blackhole', output
)
4020 self
.assertIn('id 1 blackhole', output
)
4021 self
.assertIn('id 2 blackhole', output
)
4023 # group nexthops are shown with -0 option
4025 output
= check_output('ip -0 nexthop list id 21')
4027 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4029 output
= check_output('ip -0 nexthop list id 20')
4031 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4033 output
= check_output('ip route show dev veth99 10.10.10.10')
4036 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4038 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4040 output
= check_output('ip route show dev veth99 10.10.10.11')
4043 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4045 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4047 output
= check_output('ip route show dev veth99 10.10.10.12')
4050 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4052 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4054 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4057 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4059 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4061 output
= check_output('ip route show 10.10.10.13')
4064 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4066 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4068 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4071 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4073 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4075 output
= check_output('ip route show 10.10.10.14')
4078 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4079 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4081 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4082 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4083 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4085 output
= networkctl_json()
4087 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4089 def _test_nexthop(self
, manage_foreign_nexthops
):
4090 if not manage_foreign_nexthops
:
4091 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4093 check_output('ip link add dummy98 type dummy')
4094 check_output('ip link set dummy98 up')
4095 check_output('ip address add 192.168.20.20/24 dev dummy98')
4096 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4098 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4099 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4102 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4104 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4105 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4107 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4109 remove_network_unit('25-nexthop-2.network')
4110 copy_network_unit('25-nexthop-nothing.network')
4112 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4114 output
= check_output('ip nexthop list dev veth99')
4116 self
.assertEqual(output
, '')
4117 output
= check_output('ip nexthop list dev lo')
4119 self
.assertEqual(output
, '')
4121 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4122 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4123 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4124 # here to test reconfiguring with different .network files does not trigger race.
4125 # See also comments in link_drop_requests().
4126 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4127 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4129 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4131 # Remove nexthop with ID 20
4132 check_output('ip nexthop del id 20')
4133 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4136 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4137 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4138 # hence test1 should be stuck in the configuring state.
4139 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4141 # Wait for a while, and check if the interface is still in the configuring state.
4143 output
= networkctl_status('test1')
4144 self
.assertIn('State: routable (configuring)', output
)
4146 # Check if the route which needs nexthop 20 and 21 are forgotten.
4147 output
= networkctl_json()
4149 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4151 # Reconfigure the interface that has nexthop with ID 20 and 21,
4152 # then the route requested by test1 can be configured.
4153 networkctl_reconfigure('dummy98')
4154 self
.wait_online(['test1:routable'])
4156 # Check if the requested route actually configured.
4157 output
= check_output('ip route show 10.10.11.10')
4159 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4160 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4161 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4163 remove_link('veth99')
4166 output
= check_output('ip nexthop list dev lo')
4168 self
.assertEqual(output
, '')
4170 @expectedFailureIfNexthopIsNotAvailable()
4171 def test_nexthop(self
):
4173 for manage_foreign_nexthops
in [True, False]:
4179 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4180 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4181 self
._test
_nexthop
(manage_foreign_nexthops
)
4183 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4191 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4192 def test_qdisc_cake(self
):
4193 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4195 self
.wait_online(['dummy98:routable'])
4197 output
= check_output('tc qdisc show dev dummy98')
4199 self
.assertIn('qdisc cake 3a: root', output
)
4200 self
.assertIn('bandwidth 500Mbit', output
)
4201 self
.assertIn('autorate-ingress', output
)
4202 self
.assertIn('diffserv8', output
)
4203 self
.assertIn('dual-dsthost', output
)
4204 self
.assertIn(' nat', output
)
4205 self
.assertIn(' wash', output
)
4206 self
.assertIn(' split-gso', output
)
4207 self
.assertIn(' raw', output
)
4208 self
.assertIn(' atm', output
)
4209 self
.assertIn('overhead 128', output
)
4210 self
.assertIn('mpu 20', output
)
4211 self
.assertIn('fwmark 0xff00', output
)
4212 self
.assertIn('rtt 1s', output
)
4213 self
.assertIn('ack-filter-aggressive', output
)
4215 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4216 def test_qdisc_codel(self
):
4217 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4219 self
.wait_online(['dummy98:routable'])
4221 output
= check_output('tc qdisc show dev dummy98')
4223 self
.assertRegex(output
, 'qdisc codel 33: root')
4224 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4226 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4227 def test_qdisc_drr(self
):
4228 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4230 self
.wait_online(['dummy98:routable'])
4232 output
= check_output('tc qdisc show dev dummy98')
4234 self
.assertRegex(output
, 'qdisc drr 2: root')
4235 output
= check_output('tc class show dev dummy98')
4237 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4239 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4240 def test_qdisc_ets(self
):
4241 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4243 self
.wait_online(['dummy98:routable'])
4245 output
= check_output('tc qdisc show dev dummy98')
4248 self
.assertRegex(output
, 'qdisc ets 3a: root')
4249 self
.assertRegex(output
, 'bands 10 strict 3')
4250 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4251 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4253 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4254 def test_qdisc_fq(self
):
4255 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4257 self
.wait_online(['dummy98:routable'])
4259 output
= check_output('tc qdisc show dev dummy98')
4261 self
.assertRegex(output
, 'qdisc fq 32: root')
4262 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4263 self
.assertRegex(output
, 'quantum 1500')
4264 self
.assertRegex(output
, 'initial_quantum 13000')
4265 self
.assertRegex(output
, 'maxrate 1Mbit')
4267 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4268 def test_qdisc_fq_codel(self
):
4269 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4271 self
.wait_online(['dummy98:routable'])
4273 output
= check_output('tc qdisc show dev dummy98')
4275 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4276 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')
4278 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4279 def test_qdisc_fq_pie(self
):
4280 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4282 self
.wait_online(['dummy98:routable'])
4284 output
= check_output('tc qdisc show dev dummy98')
4287 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4288 self
.assertRegex(output
, 'limit 200000p')
4290 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4291 def test_qdisc_gred(self
):
4292 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4294 self
.wait_online(['dummy98:routable'])
4296 output
= check_output('tc qdisc show dev dummy98')
4298 self
.assertRegex(output
, 'qdisc gred 38: root')
4299 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4301 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4302 def test_qdisc_hhf(self
):
4303 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4305 self
.wait_online(['dummy98:routable'])
4307 output
= check_output('tc qdisc show dev dummy98')
4309 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4310 self
.assertRegex(output
, 'limit 1022p')
4312 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4313 def test_qdisc_htb_fifo(self
):
4314 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4316 self
.wait_online(['dummy98:routable'])
4318 output
= check_output('tc qdisc show dev dummy98')
4320 self
.assertRegex(output
, 'qdisc htb 2: root')
4321 self
.assertRegex(output
, r
'default (0x30|30)')
4323 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4324 self
.assertRegex(output
, 'limit 100000p')
4326 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4327 self
.assertRegex(output
, 'limit 1000000')
4329 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4330 self
.assertRegex(output
, 'limit 1023p')
4332 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4334 output
= check_output('tc -d class show dev dummy98')
4336 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4337 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4338 # which is fixed in v6.3.0 by
4339 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4340 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4341 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4342 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4343 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4344 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4345 self
.assertRegex(output
, 'burst 123456')
4346 self
.assertRegex(output
, 'cburst 123457')
4348 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4349 def test_qdisc_ingress(self
):
4350 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4351 '25-qdisc-ingress.network', '11-dummy.netdev')
4353 self
.wait_online(['dummy98:routable', 'test1:routable'])
4355 output
= check_output('tc qdisc show dev dummy98')
4357 self
.assertRegex(output
, 'qdisc clsact')
4359 output
= check_output('tc qdisc show dev test1')
4361 self
.assertRegex(output
, 'qdisc ingress')
4363 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4364 def test_qdisc_netem(self
):
4365 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4366 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4368 self
.wait_online(['dummy98:routable', 'test1:routable'])
4370 output
= check_output('tc qdisc show dev dummy98')
4372 self
.assertRegex(output
, 'qdisc netem 30: root')
4373 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4375 output
= check_output('tc qdisc show dev test1')
4377 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4378 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4380 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4381 def test_qdisc_pie(self
):
4382 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4384 self
.wait_online(['dummy98:routable'])
4386 output
= check_output('tc qdisc show dev dummy98')
4388 self
.assertRegex(output
, 'qdisc pie 3a: root')
4389 self
.assertRegex(output
, 'limit 200000')
4391 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4392 def test_qdisc_qfq(self
):
4393 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4395 self
.wait_online(['dummy98:routable'])
4397 output
= check_output('tc qdisc show dev dummy98')
4399 self
.assertRegex(output
, 'qdisc qfq 2: root')
4400 output
= check_output('tc class show dev dummy98')
4402 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4403 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4405 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4406 def test_qdisc_sfb(self
):
4407 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4409 self
.wait_online(['dummy98:routable'])
4411 output
= check_output('tc qdisc show dev dummy98')
4413 self
.assertRegex(output
, 'qdisc sfb 39: root')
4414 self
.assertRegex(output
, 'limit 200000')
4416 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4417 def test_qdisc_sfq(self
):
4418 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4420 self
.wait_online(['dummy98:routable'])
4422 output
= check_output('tc qdisc show dev dummy98')
4424 self
.assertRegex(output
, 'qdisc sfq 36: root')
4425 self
.assertRegex(output
, 'perturb 5sec')
4427 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4428 def test_qdisc_tbf(self
):
4429 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4431 self
.wait_online(['dummy98:routable'])
4433 output
= check_output('tc qdisc show dev dummy98')
4435 self
.assertRegex(output
, 'qdisc tbf 35: root')
4436 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4438 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4439 def test_qdisc_teql(self
):
4440 call_quiet('rmmod sch_teql')
4442 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4444 self
.wait_links('dummy98')
4445 check_output('modprobe sch_teql max_equalizers=2')
4446 self
.wait_online(['dummy98:routable'])
4448 output
= check_output('tc qdisc show dev dummy98')
4450 self
.assertRegex(output
, 'qdisc teql1 31: root')
4452 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4460 def test_state_file(self
):
4461 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4463 self
.wait_online(['dummy98:routable'])
4465 # make link state file updated
4466 resolvectl('revert', 'dummy98')
4468 check_json(networkctl_json())
4470 output
= read_link_state_file('dummy98')
4472 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4473 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4474 self
.assertIn('ADMIN_STATE=configured', output
)
4475 self
.assertIn('OPER_STATE=routable', output
)
4476 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4477 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4478 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4479 self
.assertIn('ACTIVATION_POLICY=up', output
)
4480 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4481 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4482 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4483 self
.assertIn('DOMAINS=hogehoge', output
)
4484 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4485 self
.assertIn('LLMNR=no', output
)
4486 self
.assertIn('MDNS=yes', output
)
4487 self
.assertIn('DNSSEC=no', output
)
4489 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4490 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4491 resolvectl('llmnr', 'dummy98', 'yes')
4492 resolvectl('mdns', 'dummy98', 'no')
4493 resolvectl('dnssec', 'dummy98', 'yes')
4494 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4496 check_json(networkctl_json())
4498 output
= read_link_state_file('dummy98')
4500 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4501 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4502 self
.assertIn('DOMAINS=hogehogehoge', output
)
4503 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4504 self
.assertIn('LLMNR=yes', output
)
4505 self
.assertIn('MDNS=no', output
)
4506 self
.assertIn('DNSSEC=yes', output
)
4508 timedatectl('revert', 'dummy98')
4510 check_json(networkctl_json())
4512 output
= read_link_state_file('dummy98')
4514 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4515 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4516 self
.assertIn('DOMAINS=hogehogehoge', output
)
4517 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4518 self
.assertIn('LLMNR=yes', output
)
4519 self
.assertIn('MDNS=no', output
)
4520 self
.assertIn('DNSSEC=yes', output
)
4522 resolvectl('revert', 'dummy98')
4524 check_json(networkctl_json())
4526 output
= read_link_state_file('dummy98')
4528 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4529 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4530 self
.assertIn('DOMAINS=hogehoge', output
)
4531 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4532 self
.assertIn('LLMNR=no', output
)
4533 self
.assertIn('MDNS=yes', output
)
4534 self
.assertIn('DNSSEC=no', output
)
4536 def test_address_state(self
):
4537 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4540 self
.wait_online(['dummy98:degraded'])
4542 output
= read_link_state_file('dummy98')
4543 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4544 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4546 # with a routable IPv4 address
4547 check_output('ip address add 10.1.2.3/16 dev dummy98')
4548 self
.wait_online(['dummy98:routable'], ipv4
=True)
4549 self
.wait_online(['dummy98:routable'])
4551 output
= read_link_state_file('dummy98')
4552 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4553 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4555 check_output('ip address del 10.1.2.3/16 dev dummy98')
4557 # with a routable IPv6 address
4558 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4559 self
.wait_online(['dummy98:routable'], ipv6
=True)
4560 self
.wait_online(['dummy98:routable'])
4562 output
= read_link_state_file('dummy98')
4563 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4564 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4566 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4574 def test_bond_keep_master(self
):
4575 check_output('ip link add bond199 type bond mode active-backup')
4576 check_output('ip link add dummy98 type dummy')
4577 check_output('ip link set dummy98 master bond199')
4579 copy_network_unit('23-keep-master.network')
4581 self
.wait_online(['dummy98:enslaved'])
4583 output
= check_output('ip -d link show bond199')
4585 self
.assertRegex(output
, 'active_slave dummy98')
4587 output
= check_output('ip -d link show dummy98')
4589 self
.assertRegex(output
, 'master bond199')
4591 def test_bond_active_slave(self
):
4592 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4594 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4596 output
= check_output('ip -d link show bond199')
4598 self
.assertIn('active_slave dummy98', output
)
4600 def test_bond_primary_slave(self
):
4601 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4603 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4605 output
= check_output('ip -d link show bond199')
4607 self
.assertIn('primary dummy98', output
)
4610 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4611 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4612 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4613 f
.write(f
'[Link]\nMACAddress={mac}\n')
4616 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4618 output
= check_output('ip -d link show bond199')
4620 self
.assertIn(f
'link/ether {mac}', output
)
4622 def test_bond_operstate(self
):
4623 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4624 '25-bond99.network', '25-bond-slave.network')
4626 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4628 output
= check_output('ip -d link show dummy98')
4630 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4632 output
= check_output('ip -d link show test1')
4634 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4636 output
= check_output('ip -d link show bond99')
4638 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4640 self
.wait_operstate('dummy98', 'enslaved')
4641 self
.wait_operstate('test1', 'enslaved')
4642 self
.wait_operstate('bond99', 'routable')
4644 check_output('ip link set dummy98 down')
4646 self
.wait_operstate('dummy98', 'off')
4647 self
.wait_operstate('test1', 'enslaved')
4648 self
.wait_operstate('bond99', 'routable')
4650 check_output('ip link set dummy98 up')
4652 self
.wait_operstate('dummy98', 'enslaved')
4653 self
.wait_operstate('test1', 'enslaved')
4654 self
.wait_operstate('bond99', 'routable')
4656 check_output('ip link set dummy98 down')
4657 check_output('ip link set test1 down')
4659 self
.wait_operstate('dummy98', 'off')
4660 self
.wait_operstate('test1', 'off')
4662 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4663 # Huh? Kernel does not recognize that all slave interfaces are down?
4664 # Let's confirm that networkd's operstate is consistent with ip's result.
4665 self
.assertNotRegex(output
, 'NO-CARRIER')
4667 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4675 def test_bridge_mac_none(self
):
4676 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4677 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4679 self
.wait_online(['dummy98:enslaved', 'bridge99:degraded'])
4681 output
= check_output('ip link show dev dummy98')
4683 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4685 output
= check_output('ip link show dev bridge99')
4687 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4689 def test_bridge_vlan(self
):
4690 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4691 '26-bridge.netdev', '26-bridge-vlan-master.network',
4694 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4696 output
= check_output('bridge vlan show dev test1')
4698 # check if the default VID is removed
4699 self
.assertNotIn('1 Egress Untagged', output
)
4700 for i
in range(1000, 3000):
4702 self
.assertIn(f
'{i} PVID', output
)
4703 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4704 self
.assertIn(f
'{i} Egress Untagged', output
)
4705 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4706 self
.assertIn(f
'{i}', output
)
4708 self
.assertNotIn(f
'{i}', output
)
4710 output
= check_output('bridge vlan show dev bridge99')
4712 # check if the default VID is removed
4713 self
.assertNotIn('1 Egress Untagged', output
)
4714 for i
in range(1000, 3000):
4716 self
.assertIn(f
'{i} PVID', output
)
4717 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4718 self
.assertIn(f
'{i} Egress Untagged', output
)
4719 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4720 self
.assertIn(f
'{i}', output
)
4722 self
.assertNotIn(f
'{i}', output
)
4725 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4726 '26-bridge-vlan-master.network.d/10-override.conf')
4728 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4730 output
= check_output('bridge vlan show dev test1')
4732 for i
in range(1000, 3000):
4734 self
.assertIn(f
'{i} PVID', output
)
4735 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4736 self
.assertIn(f
'{i} Egress Untagged', output
)
4737 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4738 self
.assertIn(f
'{i}', output
)
4740 self
.assertNotIn(f
'{i}', output
)
4742 output
= check_output('bridge vlan show dev bridge99')
4744 for i
in range(1000, 3000):
4746 self
.assertIn(f
'{i} PVID', output
)
4747 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4748 self
.assertIn(f
'{i} Egress Untagged', output
)
4749 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4750 self
.assertIn(f
'{i}', output
)
4752 self
.assertNotIn(f
'{i}', output
)
4754 # Remove several vlan IDs
4755 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4756 '26-bridge-vlan-master.network.d/20-override.conf')
4758 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4760 output
= check_output('bridge vlan show dev test1')
4762 for i
in range(1000, 3000):
4764 self
.assertIn(f
'{i} PVID', output
)
4765 elif i
in range(2012, 2016):
4766 self
.assertIn(f
'{i} Egress Untagged', output
)
4767 elif i
in range(2008, 2014):
4768 self
.assertIn(f
'{i}', output
)
4770 self
.assertNotIn(f
'{i}', output
)
4772 output
= check_output('bridge vlan show dev bridge99')
4774 for i
in range(1000, 3000):
4776 self
.assertIn(f
'{i} PVID', output
)
4777 elif i
in range(2022, 2026):
4778 self
.assertIn(f
'{i} Egress Untagged', output
)
4779 elif i
in range(2018, 2024):
4780 self
.assertIn(f
'{i}', output
)
4782 self
.assertNotIn(f
'{i}', output
)
4784 # Remove all vlan IDs
4785 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4786 '26-bridge-vlan-master.network.d/30-override.conf')
4788 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4790 output
= check_output('bridge vlan show dev test1')
4792 self
.assertNotIn('PVID', output
)
4793 for i
in range(1000, 3000):
4794 self
.assertNotIn(f
'{i}', output
)
4796 output
= check_output('bridge vlan show dev bridge99')
4798 self
.assertNotIn('PVID', output
)
4799 for i
in range(1000, 3000):
4800 self
.assertNotIn(f
'{i}', output
)
4802 def test_bridge_vlan_issue_20373(self
):
4803 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4804 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4805 '21-vlan.netdev', '21-vlan.network')
4807 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4809 output
= check_output('bridge vlan show dev test1')
4811 self
.assertIn('100 PVID Egress Untagged', output
)
4812 self
.assertIn('560', output
)
4813 self
.assertIn('600', output
)
4815 output
= check_output('bridge vlan show dev bridge99')
4817 self
.assertIn('1 PVID Egress Untagged', output
)
4818 self
.assertIn('100', output
)
4819 self
.assertIn('600', output
)
4821 def test_bridge_mdb(self
):
4822 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4823 '26-bridge.netdev', '26-bridge-mdb-master.network')
4825 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4827 output
= check_output('bridge mdb show dev bridge99')
4829 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4830 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4832 # Old kernel may not support bridge MDB entries on bridge master
4833 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4834 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4835 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4837 def test_bridge_keep_master(self
):
4838 check_output('ip link add bridge99 type bridge')
4839 check_output('ip link set bridge99 up')
4840 check_output('ip link add dummy98 type dummy')
4841 check_output('ip link set dummy98 master bridge99')
4843 copy_network_unit('23-keep-master.network')
4845 self
.wait_online(['dummy98:enslaved'])
4847 output
= check_output('ip -d link show dummy98')
4849 self
.assertRegex(output
, 'master bridge99')
4850 self
.assertRegex(output
, 'bridge')
4852 output
= check_output('bridge -d link show dummy98')
4854 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4855 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4856 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4857 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4858 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4859 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4860 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4861 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4862 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4863 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4864 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4865 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4867 def test_bridge_property(self
):
4868 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4869 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4870 '25-bridge99.network')
4872 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4874 output
= check_output('ip -d link show bridge99')
4876 self
.assertIn('mtu 9000 ', output
)
4878 output
= check_output('ip -d link show test1')
4880 self
.assertIn('master bridge99 ', output
)
4881 self
.assertIn('bridge_slave', output
)
4882 self
.assertIn('mtu 9000 ', output
)
4884 output
= check_output('ip -d link show dummy98')
4886 self
.assertIn('master bridge99 ', output
)
4887 self
.assertIn('bridge_slave', output
)
4888 self
.assertIn('mtu 9000 ', output
)
4890 output
= check_output('ip addr show bridge99')
4892 self
.assertIn('192.168.0.15/24', output
)
4894 output
= check_output('bridge -d link show dummy98')
4896 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4897 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4898 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4899 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4900 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4901 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4902 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4903 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4904 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4905 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4906 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4907 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4908 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4910 output
= check_output('bridge -d link show test1')
4912 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4914 check_output('ip address add 192.168.0.16/24 dev bridge99')
4915 output
= check_output('ip addr show bridge99')
4917 self
.assertIn('192.168.0.16/24', output
)
4920 print('### ip -6 route list table all dev bridge99')
4921 output
= check_output('ip -6 route list table all dev bridge99')
4923 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4925 remove_link('test1')
4926 self
.wait_operstate('bridge99', 'routable')
4928 output
= check_output('ip -d link show bridge99')
4930 self
.assertIn('mtu 9000 ', output
)
4932 output
= check_output('ip -d link show dummy98')
4934 self
.assertIn('master bridge99 ', output
)
4935 self
.assertIn('bridge_slave', output
)
4936 self
.assertIn('mtu 9000 ', output
)
4938 remove_link('dummy98')
4939 self
.wait_operstate('bridge99', 'no-carrier')
4941 output
= check_output('ip -d link show bridge99')
4943 # When no carrier, the kernel may reset the MTU
4944 self
.assertIn('NO-CARRIER', output
)
4946 output
= check_output('ip address show bridge99')
4948 self
.assertNotIn('192.168.0.15/24', output
)
4949 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4951 print('### ip -6 route list table all dev bridge99')
4952 output
= check_output('ip -6 route list table all dev bridge99')
4954 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4956 check_output('ip link add dummy98 type dummy')
4957 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4959 output
= check_output('ip -d link show bridge99')
4961 self
.assertIn('mtu 9000 ', output
)
4963 output
= check_output('ip -d link show dummy98')
4965 self
.assertIn('master bridge99 ', output
)
4966 self
.assertIn('bridge_slave', output
)
4967 self
.assertIn('mtu 9000 ', output
)
4969 def test_bridge_configure_without_carrier(self
):
4970 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4974 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4975 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4976 with self
.subTest(test
=test
):
4977 if test
== 'no-slave':
4978 # bridge has no slaves; it's up but *might* not have carrier
4979 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4980 # due to a bug in the kernel, newly-created bridges are brought up
4981 # *with* carrier, unless they have had any setting changed; e.g.
4982 # their mac set, priority set, etc. Then, they will lose carrier
4983 # as soon as a (down) slave interface is added, and regain carrier
4984 # again once the slave interface is brought up.
4985 #self.check_link_attr('bridge99', 'carrier', '0')
4986 elif test
== 'add-slave':
4987 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4988 self
.check_link_attr('test1', 'operstate', 'down')
4989 check_output('ip link set dev test1 master bridge99')
4990 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4991 self
.check_link_attr('bridge99', 'carrier', '0')
4992 elif test
== 'slave-up':
4993 # bring up slave, which will have carrier; bridge gains carrier
4994 check_output('ip link set dev test1 up')
4995 self
.wait_online(['bridge99:routable'])
4996 self
.check_link_attr('bridge99', 'carrier', '1')
4997 elif test
== 'slave-no-carrier':
4998 # drop slave carrier; bridge loses carrier
4999 check_output('ip link set dev test1 carrier off')
5000 self
.wait_online(['bridge99:no-carrier:no-carrier'])
5001 self
.check_link_attr('bridge99', 'carrier', '0')
5002 elif test
== 'slave-carrier':
5003 # restore slave carrier; bridge gains carrier
5004 check_output('ip link set dev test1 carrier on')
5005 self
.wait_online(['bridge99:routable'])
5006 self
.check_link_attr('bridge99', 'carrier', '1')
5007 elif test
== 'slave-down':
5008 # bring down slave; bridge loses carrier
5009 check_output('ip link set dev test1 down')
5010 self
.wait_online(['bridge99:no-carrier:no-carrier'])
5011 self
.check_link_attr('bridge99', 'carrier', '0')
5013 output
= networkctl_status('bridge99')
5014 self
.assertRegex(output
, '10.1.2.3')
5015 self
.assertRegex(output
, '10.1.2.1')
5017 def test_bridge_ignore_carrier_loss(self
):
5018 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5019 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5020 '25-bridge99-ignore-carrier-loss.network')
5022 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
5024 check_output('ip address add 192.168.0.16/24 dev bridge99')
5025 remove_link('test1', 'dummy98')
5028 output
= check_output('ip address show bridge99')
5030 self
.assertRegex(output
, 'NO-CARRIER')
5031 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5032 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5034 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5035 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5036 '25-bridge99-ignore-carrier-loss.network')
5038 self
.wait_online(['bridge99:no-carrier'])
5040 for trial
in range(4):
5041 check_output('ip link add dummy98 type dummy')
5042 check_output('ip link set dummy98 up')
5044 remove_link('dummy98')
5046 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
5048 output
= check_output('ip address show bridge99')
5050 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5052 output
= check_output('ip rule list table 100')
5054 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5056 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5064 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5065 def test_sriov(self
):
5066 copy_network_unit('25-default.link', '25-sriov.network')
5068 call('modprobe netdevsim')
5070 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5073 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5077 self
.wait_online(['eni99np1:routable'])
5079 output
= check_output('ip link show dev eni99np1')
5081 self
.assertRegex(output
,
5082 '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 *'
5083 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5084 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5087 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5088 def test_sriov_udev(self
):
5089 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5091 call('modprobe netdevsim')
5093 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5097 self
.wait_online(['eni99np1:routable'])
5099 # the name eni99np1 may be an alternative name.
5100 ifname
= link_resolve('eni99np1')
5102 output
= check_output('ip link show dev eni99np1')
5104 self
.assertRegex(output
,
5105 '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 *'
5106 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5107 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5109 self
.assertNotIn('vf 3', output
)
5110 self
.assertNotIn('vf 4', output
)
5112 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5113 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5116 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5118 output
= check_output('ip link show dev eni99np1')
5120 self
.assertRegex(output
,
5121 '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 *'
5122 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5123 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5126 self
.assertNotIn('vf 4', output
)
5128 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5129 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5132 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5134 output
= check_output('ip link show dev eni99np1')
5136 self
.assertRegex(output
,
5137 '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 *'
5138 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5139 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5142 self
.assertNotIn('vf 4', output
)
5144 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5145 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5148 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5150 output
= check_output('ip link show dev eni99np1')
5152 self
.assertRegex(output
,
5153 '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 *'
5154 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5156 self
.assertNotIn('vf 2', output
)
5157 self
.assertNotIn('vf 3', output
)
5158 self
.assertNotIn('vf 4', output
)
5160 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5161 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5164 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5166 output
= check_output('ip link show dev eni99np1')
5168 self
.assertRegex(output
,
5169 '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 *'
5170 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5171 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5173 self
.assertNotIn('vf 3', output
)
5174 self
.assertNotIn('vf 4', output
)
5176 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5184 def test_lldp(self
):
5185 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5187 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
5189 for trial
in range(10):
5193 output
= networkctl('lldp')
5195 if re
.search(r
'veth99 .* veth-peer', output
):
5200 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5208 def test_ipv6_prefix_delegation(self
):
5209 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5210 self
.setup_nftset('addr6', 'ipv6_addr')
5211 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5212 self
.setup_nftset('ifindex', 'iface_index')
5214 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5216 output
= resolvectl('dns', 'veth99')
5218 self
.assertRegex(output
, 'fe80::')
5219 self
.assertRegex(output
, '2002:da8:1::1')
5221 output
= resolvectl('domain', 'veth99')
5223 self
.assertIn('hogehoge.test', output
)
5225 output
= networkctl_status('veth99')
5227 self
.assertRegex(output
, '2002:da8:1:0')
5229 self
.check_netlabel('veth99', '2002:da8:1::/64')
5230 self
.check_netlabel('veth99', '2002:da8:2::/64')
5232 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5233 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5234 self
.check_nftset('network6', '2002:da8:1::/64')
5235 self
.check_nftset('network6', '2002:da8:2::/64')
5236 self
.check_nftset('ifindex', 'veth99')
5238 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5240 def check_ipv6_token_static(self
):
5241 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5243 output
= networkctl_status('veth99')
5245 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5246 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5247 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5248 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5250 def test_ipv6_token_static(self
):
5251 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5254 self
.check_ipv6_token_static()
5257 check_output('ip link set veth99 down')
5258 check_output('ip link set veth99 up')
5260 self
.check_ipv6_token_static()
5263 check_output('ip link set veth99 down')
5264 time
.sleep(random
.uniform(0, 0.1))
5265 check_output('ip link set veth99 up')
5266 time
.sleep(random
.uniform(0, 0.1))
5268 self
.check_ipv6_token_static()
5270 def test_ipv6_token_prefixstable(self
):
5271 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5273 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5275 output
= networkctl_status('veth99')
5277 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5278 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5280 def test_ipv6_token_prefixstable_without_address(self
):
5281 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5283 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5285 output
= networkctl_status('veth99')
5287 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5288 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5290 def test_router_preference(self
):
5291 copy_network_unit('25-veth-client.netdev',
5292 '25-veth-router-high.netdev',
5293 '25-veth-router-low.netdev',
5295 '25-veth-bridge.network',
5296 '25-veth-client.network',
5297 '25-veth-router-high.network',
5298 '25-veth-router-low.network',
5299 '25-bridge99.network')
5301 self
.wait_online(['client-p:enslaved',
5302 'router-high:degraded', 'router-high-p:enslaved',
5303 'router-low:degraded', 'router-low-p:enslaved',
5304 'bridge99:routable'])
5306 networkctl_reconfigure('client')
5307 self
.wait_online(['client:routable'])
5309 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5310 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5311 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5312 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5314 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5316 self
.assertIn('pref high', output
)
5317 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5319 self
.assertIn('pref low', output
)
5321 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5322 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5325 self
.wait_online(['client:routable'])
5327 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5328 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5329 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5330 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5332 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5334 self
.assertIn('pref high', output
)
5335 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5337 self
.assertIn('pref low', output
)
5339 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5340 def test_captive_portal(self
):
5341 copy_network_unit('25-veth-client.netdev',
5342 '25-veth-router-captive.netdev',
5344 '25-veth-client-captive.network',
5345 '25-veth-router-captive.network',
5346 '25-veth-bridge-captive.network',
5347 '25-bridge99.network')
5349 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5350 'router-captive:degraded', 'router-captivep:enslaved'])
5352 start_radvd(config_file
='captive-portal.conf')
5353 networkctl_reconfigure('client')
5354 self
.wait_online(['client:routable'])
5356 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5357 output
= networkctl_status('client')
5359 self
.assertIn('Captive Portal: http://systemd.io', output
)
5361 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5362 def test_invalid_captive_portal(self
):
5363 def radvd_write_config(captive_portal_uri
):
5364 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5365 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5367 captive_portal_uris
= [
5368 "42ěščěškd ěšč ě s",
5373 copy_network_unit('25-veth-client.netdev',
5374 '25-veth-router-captive.netdev',
5376 '25-veth-client-captive.network',
5377 '25-veth-router-captive.network',
5378 '25-veth-bridge-captive.network',
5379 '25-bridge99.network')
5381 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5382 'router-captive:degraded', 'router-captivep:enslaved'])
5384 for uri
in captive_portal_uris
:
5385 print(f
"Captive portal: {uri}")
5386 radvd_write_config(uri
)
5388 start_radvd(config_file
='bogus-captive-portal.conf')
5389 networkctl_reconfigure('client')
5390 self
.wait_online(['client:routable'])
5392 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5393 output
= networkctl_status('client')
5395 self
.assertNotIn('Captive Portal:', output
)
5397 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5405 def test_dhcp_server(self
):
5406 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5408 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5410 output
= networkctl_status('veth99')
5412 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5413 self
.assertIn('Gateway: 192.168.5.3', output
)
5414 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5415 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5417 output
= networkctl_status('veth-peer')
5418 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5420 def test_dhcp_server_null_server_address(self
):
5421 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5423 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5425 output
= check_output('ip --json address show dev veth-peer')
5426 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5427 print(server_address
)
5429 output
= check_output('ip --json address show dev veth99')
5430 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5431 print(client_address
)
5433 output
= networkctl_status('veth99')
5435 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5436 self
.assertIn(f
'Gateway: {server_address}', output
)
5437 self
.assertIn(f
'DNS: {server_address}', output
)
5438 self
.assertIn(f
'NTP: {server_address}', output
)
5440 output
= networkctl_status('veth-peer')
5441 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5443 def test_dhcp_server_with_uplink(self
):
5444 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5445 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5447 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5449 output
= networkctl_status('veth99')
5451 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5452 self
.assertIn('Gateway: 192.168.5.3', output
)
5453 self
.assertIn('DNS: 192.168.5.1', output
)
5454 self
.assertIn('NTP: 192.168.5.1', output
)
5456 def test_emit_router_timezone(self
):
5457 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5459 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5461 output
= networkctl_status('veth99')
5463 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5464 self
.assertIn('Gateway: 192.168.5.1', output
)
5465 self
.assertIn('Time Zone: Europe/Berlin', output
)
5467 def test_dhcp_server_static_lease(self
):
5468 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5470 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5472 output
= networkctl_status('veth99')
5474 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5475 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5477 def test_dhcp_server_static_lease_default_client_id(self
):
5478 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5480 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5482 output
= networkctl_status('veth99')
5484 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5485 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5487 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5495 def test_relay_agent(self
):
5496 copy_network_unit('25-agent-veth-client.netdev',
5497 '25-agent-veth-server.netdev',
5498 '25-agent-client.network',
5499 '25-agent-server.network',
5500 '25-agent-client-peer.network',
5501 '25-agent-server-peer.network')
5504 self
.wait_online(['client:routable'])
5506 output
= networkctl_status('client')
5508 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5510 def test_replay_agent_on_bridge(self
):
5511 copy_network_unit('25-agent-bridge.netdev',
5512 '25-agent-veth-client.netdev',
5513 '25-agent-bridge.network',
5514 '25-agent-bridge-port.network',
5515 '25-agent-client.network')
5517 self
.wait_online(['bridge-relay:routable', 'client-peer:enslaved'])
5520 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5522 if expect
in read_networkd_log():
5528 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5536 def test_dhcp_client_ipv6_only(self
):
5537 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5540 self
.wait_online(['veth-peer:carrier'])
5542 # information request mode
5543 # The name ipv6-only option may not be supported by older dnsmasq
5544 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5545 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5546 '--dhcp-option=option6:dns-server,[2600::ee]',
5547 '--dhcp-option=option6:ntp-server,[2600::ff]',
5548 ra_mode
='ra-stateless')
5549 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5551 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5552 # Let's wait for the expected DNS server being listed in the state file.
5553 for _
in range(100):
5554 output
= read_link_state_file('veth99')
5555 if 'DNS=2600::ee' in output
:
5559 # Check link state file
5560 print('## link state file')
5561 output
= read_link_state_file('veth99')
5563 self
.assertIn('DNS=2600::ee', output
)
5564 self
.assertIn('NTP=2600::ff', output
)
5566 # Check manager state file
5567 print('## manager state file')
5568 output
= read_manager_state_file()
5570 self
.assertRegex(output
, 'DNS=.*2600::ee')
5571 self
.assertRegex(output
, 'NTP=.*2600::ff')
5573 print('## dnsmasq log')
5574 output
= read_dnsmasq_log_file()
5576 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5577 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5578 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5579 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5580 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5583 check_json(networkctl_json('veth99'))
5587 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5588 '--dhcp-option=option6:dns-server,[2600::ee]',
5589 '--dhcp-option=option6:ntp-server,[2600::ff]')
5590 networkctl_reconfigure('veth99')
5591 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5594 output
= check_output('ip address show dev veth99 scope global')
5596 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5597 self
.assertNotIn('192.168.5', output
)
5599 # checking semi-static route
5600 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5602 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5604 # Confirm that ipv6 token is not set in the kernel
5605 output
= check_output('ip token show dev veth99')
5607 self
.assertRegex(output
, 'token :: dev veth99')
5609 # Make manager and link state file updated
5610 resolvectl('revert', 'veth99')
5612 # Check link state file
5613 print('## link state file')
5614 output
= read_link_state_file('veth99')
5616 self
.assertIn('DNS=2600::ee', output
)
5617 self
.assertIn('NTP=2600::ff', output
)
5619 # Check manager state file
5620 print('## manager state file')
5621 output
= read_manager_state_file()
5623 self
.assertRegex(output
, 'DNS=.*2600::ee')
5624 self
.assertRegex(output
, 'NTP=.*2600::ff')
5626 print('## dnsmasq log')
5627 output
= read_dnsmasq_log_file()
5629 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5630 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5631 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5632 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5633 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5634 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5637 check_json(networkctl_json('veth99'))
5639 # Testing without rapid commit support
5640 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5641 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5644 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5645 '--dhcp-option=option6:dns-server,[2600::ee]',
5646 '--dhcp-option=option6:ntp-server,[2600::ff]')
5649 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5652 output
= check_output('ip address show dev veth99 scope global')
5654 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5655 self
.assertNotIn('192.168.5', output
)
5657 # checking semi-static route
5658 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5660 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5662 # Make manager and link state file updated
5663 resolvectl('revert', 'veth99')
5665 # Check link state file
5666 print('## link state file')
5667 output
= read_link_state_file('veth99')
5669 self
.assertIn('DNS=2600::ee', output
)
5670 self
.assertIn('NTP=2600::ff', output
)
5672 # Check manager state file
5673 print('## manager state file')
5674 output
= read_manager_state_file()
5676 self
.assertRegex(output
, 'DNS=.*2600::ee')
5677 self
.assertRegex(output
, 'NTP=.*2600::ff')
5679 print('## dnsmasq log')
5680 output
= read_dnsmasq_log_file()
5682 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5683 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5684 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5685 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5686 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5687 self
.assertNotIn('rapid-commit', output
)
5690 check_json(networkctl_json('veth99'))
5692 def test_dhcp_client_ipv6_dbus_status(self
):
5693 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5695 self
.wait_online(['veth-peer:carrier'])
5697 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5698 # bit set) has yet been received and the configuration does not include WithoutRA=true
5699 state
= get_dhcp6_client_state('veth99')
5700 print(f
"DHCPv6 client state = {state}")
5701 self
.assertEqual(state
, 'stopped')
5703 state
= get_dhcp4_client_state('veth99')
5704 print(f
"DHCPv4 client state = {state}")
5705 self
.assertEqual(state
, 'selecting')
5707 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5708 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5710 state
= get_dhcp6_client_state('veth99')
5711 print(f
"DHCPv6 client state = {state}")
5712 self
.assertEqual(state
, 'bound')
5714 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5715 for _
in range(100):
5716 state
= get_dhcp4_client_state('veth99')
5717 if state
== 'stopped':
5721 print(f
"DHCPv4 client state = {state}")
5722 self
.assertEqual(state
, 'stopped')
5724 # restart dnsmasq to clear log
5726 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5728 # Test renew command
5729 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5730 networkctl('renew', 'veth99')
5732 for _
in range(100):
5733 state
= get_dhcp4_client_state('veth99')
5734 if state
== 'stopped':
5738 print(f
"DHCPv4 client state = {state}")
5739 self
.assertEqual(state
, 'stopped')
5741 print('## dnsmasq log')
5742 output
= read_dnsmasq_log_file()
5744 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5745 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5746 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5747 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5749 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5750 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5753 self
.wait_online(['veth-peer:carrier'])
5755 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5758 output
= check_output('ip address show dev veth99 scope global')
5760 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5761 self
.assertNotIn('192.168.5', output
)
5763 print('## dnsmasq log')
5764 output
= read_dnsmasq_log_file()
5766 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5767 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5768 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5769 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5770 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5772 def test_dhcp_client_ipv4_only(self
):
5773 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5775 self
.setup_nftset('addr4', 'ipv4_addr')
5776 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5777 self
.setup_nftset('ifindex', 'iface_index')
5780 self
.wait_online(['veth-peer:carrier'])
5781 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5782 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5783 '--dhcp-option=option:domain-search,example.com',
5784 '--dhcp-alternate-port=67,5555',
5785 ipv4_range
='192.168.5.110,192.168.5.119')
5786 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5787 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5789 print('## ip address show dev veth99 scope global')
5790 output
= check_output('ip address show dev veth99 scope global')
5792 self
.assertIn('mtu 1492', output
)
5793 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5794 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')
5795 self
.assertNotIn('2600::', output
)
5797 output
= check_output('ip -4 --json address show dev veth99')
5798 for i
in json
.loads(output
)[0]['addr_info']:
5799 if i
['label'] == 'test-label':
5800 address1
= i
['local']
5803 self
.assertFalse(True)
5805 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5807 print('## ip route show table main dev veth99')
5808 output
= check_output('ip route show table main dev veth99')
5810 # no DHCP routes assigned to the main table
5811 self
.assertNotIn('proto dhcp', output
)
5813 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5814 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5815 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5816 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5818 print('## ip route show table 211 dev veth99')
5819 output
= check_output('ip route show table 211 dev veth99')
5821 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5822 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5823 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5824 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5825 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5826 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5828 print('## link state file')
5829 output
= read_link_state_file('veth99')
5831 # checking DNS server, SIP server, and Domains
5832 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5833 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5834 self
.assertIn('DOMAINS=example.com', output
)
5837 j
= json
.loads(networkctl_json('veth99'))
5839 self
.assertEqual(len(j
['DNS']), 2)
5842 self
.assertEqual(i
['Family'], 2)
5843 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5844 self
.assertRegex(a
, '^192.168.5.[67]$')
5845 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5846 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5847 self
.assertEqual('192.168.5.1', a
)
5849 self
.assertEqual(len(j
['SIP']), 2)
5852 self
.assertEqual(i
['Family'], 2)
5853 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5854 self
.assertRegex(a
, '^192.168.5.2[12]$')
5855 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5856 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5857 self
.assertEqual('192.168.5.1', a
)
5859 print('## dnsmasq log')
5860 output
= read_dnsmasq_log_file()
5862 self
.assertIn('vendor class: FooBarVendorTest', output
)
5863 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5864 self
.assertIn('client provides name: test-hostname', output
)
5865 self
.assertIn('26:mtu', output
)
5867 # change address range, DNS servers, and Domains
5869 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5870 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5871 '--dhcp-option=option:domain-search,foo.example.com',
5872 '--dhcp-alternate-port=67,5555',
5873 ipv4_range
='192.168.5.120,192.168.5.129',)
5875 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5876 print('Wait for the DHCP lease to be expired')
5877 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5878 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5880 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5882 print('## ip address show dev veth99 scope global')
5883 output
= check_output('ip address show dev veth99 scope global')
5885 self
.assertIn('mtu 1492', output
)
5886 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5887 self
.assertNotIn(f
'{address1}', output
)
5888 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')
5889 self
.assertNotIn('2600::', output
)
5891 output
= check_output('ip -4 --json address show dev veth99')
5892 for i
in json
.loads(output
)[0]['addr_info']:
5893 if i
['label'] == 'test-label':
5894 address2
= i
['local']
5897 self
.assertFalse(True)
5899 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5901 print('## ip route show table main dev veth99')
5902 output
= check_output('ip route show table main dev veth99')
5904 # no DHCP routes assigned to the main table
5905 self
.assertNotIn('proto dhcp', output
)
5907 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5908 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5909 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5910 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5912 print('## ip route show table 211 dev veth99')
5913 output
= check_output('ip route show table 211 dev veth99')
5915 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5916 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5917 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5918 self
.assertNotIn('192.168.5.6', output
)
5919 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5920 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5921 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5923 print('## link state file')
5924 output
= read_link_state_file('veth99')
5926 # checking DNS server, SIP server, and Domains
5927 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5928 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5929 self
.assertIn('DOMAINS=foo.example.com', output
)
5932 j
= json
.loads(networkctl_json('veth99'))
5934 self
.assertEqual(len(j
['DNS']), 3)
5937 self
.assertEqual(i
['Family'], 2)
5938 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5939 self
.assertRegex(a
, '^192.168.5.[178]$')
5940 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5941 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5942 self
.assertEqual('192.168.5.1', a
)
5944 self
.assertEqual(len(j
['SIP']), 2)
5947 self
.assertEqual(i
['Family'], 2)
5948 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5949 self
.assertRegex(a
, '^192.168.5.2[34]$')
5950 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5951 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5952 self
.assertEqual('192.168.5.1', a
)
5954 print('## dnsmasq log')
5955 output
= read_dnsmasq_log_file()
5957 self
.assertIn('vendor class: FooBarVendorTest', output
)
5958 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5959 self
.assertIn('client provides name: test-hostname', output
)
5960 self
.assertIn('26:mtu', output
)
5962 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5964 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5965 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5966 self
.check_nftset('ifindex', 'veth99')
5968 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5970 def test_dhcp_client_ipv4_dbus_status(self
):
5971 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5973 self
.wait_online(['veth-peer:carrier'])
5975 state
= get_dhcp4_client_state('veth99')
5976 print(f
"State = {state}")
5977 self
.assertEqual(state
, 'rebooting')
5979 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5980 '--dhcp-option=option:domain-search,example.com',
5981 '--dhcp-alternate-port=67,5555',
5982 ipv4_range
='192.168.5.110,192.168.5.119')
5983 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5984 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5986 state
= get_dhcp4_client_state('veth99')
5987 print(f
"State = {state}")
5988 self
.assertEqual(state
, 'bound')
5990 def test_dhcp_client_allow_list(self
):
5991 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5994 self
.wait_online(['veth-peer:carrier'])
5995 since
= datetime
.datetime
.now()
5998 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6000 if expect
in read_networkd_log(since
=since
):
6006 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6007 since
= datetime
.datetime
.now()
6010 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6012 if expect
in read_networkd_log(since
=since
):
6018 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6019 since
= datetime
.datetime
.now()
6022 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6024 if expect
in read_networkd_log(since
=since
):
6030 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6031 def test_dhcp_client_rapid_commit(self
):
6032 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6034 self
.wait_online(['veth-peer:carrier'])
6036 start_dnsmasq('--dhcp-rapid-commit')
6037 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6038 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6040 state
= get_dhcp4_client_state('veth99')
6041 print(f
"DHCPv4 client state = {state}")
6042 self
.assertEqual(state
, 'bound')
6044 output
= read_dnsmasq_log_file()
6045 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6046 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6047 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6048 self
.assertIn('DHCPACK(veth-peer)', output
)
6050 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6051 copy_network_unit('25-veth.netdev',
6052 '25-dhcp-server-ipv6-only-mode.network',
6053 '25-dhcp-client-ipv6-only-mode.network')
6055 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
6056 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6058 state
= get_dhcp4_client_state('veth99')
6059 print(f
"State = {state}")
6060 self
.assertEqual(state
, 'bound')
6062 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6064 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6070 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6071 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6072 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6074 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6075 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6076 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6077 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6078 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6079 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6080 copy_network_unit(*testunits
, copy_dropins
=False)
6083 self
.wait_online(['veth-peer:carrier'])
6084 additional_options
= [
6085 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6086 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6087 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6090 additional_options
+= [
6091 '--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'
6093 start_dnsmasq(*additional_options
)
6094 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6096 output
= check_output('ip -4 route show dev veth99')
6102 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6103 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6104 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6105 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6106 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6108 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6109 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6110 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6111 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6113 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6114 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6115 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6116 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6117 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6118 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6119 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6120 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6123 if use_gateway
and (not classless
or not use_routes
):
6124 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6126 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6128 # Check route to gateway
6129 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6130 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6132 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6134 # Check RoutesToDNS= and RoutesToNTP=
6135 if dns_and_ntp_routes
:
6136 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6137 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6140 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6141 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6143 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6144 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6146 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6147 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6149 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6150 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6151 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6152 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6154 check_json(networkctl_json())
6156 def test_dhcp_client_settings_anonymize(self
):
6157 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6159 self
.wait_online(['veth-peer:carrier'])
6161 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6163 print('## dnsmasq log')
6164 output
= read_dnsmasq_log_file()
6166 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6167 self
.assertNotIn('test-hostname', output
)
6168 self
.assertNotIn('26:mtu', output
)
6170 def test_dhcp_keep_configuration_dhcp(self
):
6171 copy_network_unit('25-veth.netdev',
6172 '25-dhcp-server-veth-peer.network',
6173 '25-dhcp-client-keep-configuration-dhcp.network')
6175 self
.wait_online(['veth-peer:carrier'])
6177 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6179 output
= check_output('ip address show dev veth99 scope global')
6181 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6182 'valid_lft forever preferred_lft forever')
6184 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6187 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6188 print('Wait for the DHCP lease to be expired')
6191 # The lease address should be kept after the lease expired
6192 output
= check_output('ip address show dev veth99 scope global')
6194 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6195 'valid_lft forever preferred_lft forever')
6199 # The lease address should be kept after networkd stopped
6200 output
= check_output('ip address show dev veth99 scope global')
6202 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6203 'valid_lft forever preferred_lft forever')
6205 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6206 f
.write('[Network]\nDHCP=no\n')
6209 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6211 # Still the lease address should be kept after networkd restarted
6212 output
= check_output('ip address show dev veth99 scope global')
6214 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6215 'valid_lft forever preferred_lft forever')
6217 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6218 copy_network_unit('25-veth.netdev',
6219 '25-dhcp-server-veth-peer.network',
6220 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6222 self
.wait_online(['veth-peer:carrier'])
6224 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6226 output
= check_output('ip address show dev veth99 scope global')
6228 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6233 output
= check_output('ip address show dev veth99 scope global')
6235 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6238 self
.wait_online(['veth-peer:routable'])
6240 output
= check_output('ip address show dev veth99 scope global')
6242 self
.assertNotIn('192.168.5.', output
)
6244 def test_dhcp_client_reuse_address_as_static(self
):
6245 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6247 self
.wait_online(['veth-peer:carrier'])
6249 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6251 # link become 'routable' when at least one protocol provide an valid address.
6252 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6253 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6255 output
= check_output('ip address show dev veth99 scope global')
6256 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6257 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6258 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6259 print(static_network
)
6261 remove_network_unit('25-dhcp-client.network')
6263 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6264 f
.write(static_network
)
6267 self
.wait_online(['veth99:routable'])
6269 output
= check_output('ip -4 address show dev veth99 scope global')
6271 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6272 'valid_lft forever preferred_lft forever')
6274 output
= check_output('ip -6 address show dev veth99 scope global')
6276 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6277 'valid_lft forever preferred_lft forever')
6279 @expectedFailureIfModuleIsNotAvailable('vrf')
6280 def test_dhcp_client_vrf(self
):
6281 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6282 '25-vrf.netdev', '25-vrf.network')
6284 self
.wait_online(['veth-peer:carrier'])
6286 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
6288 # link become 'routable' when at least one protocol provide an valid address.
6289 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6290 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6292 print('## ip -d link show dev vrf99')
6293 output
= check_output('ip -d link show dev vrf99')
6295 self
.assertRegex(output
, 'vrf table 42')
6297 print('## ip address show vrf vrf99')
6298 output
= check_output('ip address show vrf vrf99')
6300 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6301 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6302 self
.assertRegex(output
, 'inet6 .* scope link')
6304 print('## ip address show dev veth99')
6305 output
= check_output('ip address show dev veth99')
6307 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6308 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6309 self
.assertRegex(output
, 'inet6 .* scope link')
6311 print('## ip route show vrf vrf99')
6312 output
= check_output('ip route show vrf vrf99')
6314 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6315 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6316 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6318 print('## ip route show table main dev veth99')
6319 output
= check_output('ip route show table main dev veth99')
6321 self
.assertEqual(output
, '')
6323 def test_dhcp_client_gateway_onlink_implicit(self
):
6324 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6325 '25-dhcp-client-gateway-onlink-implicit.network')
6327 self
.wait_online(['veth-peer:carrier'])
6329 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6331 output
= networkctl_status('veth99')
6333 self
.assertRegex(output
, '192.168.5')
6335 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6337 self
.assertRegex(output
, 'onlink')
6338 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6340 self
.assertRegex(output
, 'onlink')
6342 def test_dhcp_client_with_ipv4ll(self
):
6343 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6344 '25-dhcp-client-with-ipv4ll.network')
6346 # we need to increase timeout above default, as this will need to wait for
6347 # systemd-networkd to get the dhcpv4 transient failure event
6348 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6350 output
= check_output('ip -4 address show dev veth99')
6352 self
.assertNotIn('192.168.5.', output
)
6353 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6356 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6357 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6358 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')
6359 self
.wait_online(['veth99:routable'])
6361 output
= check_output('ip -4 address show dev veth99')
6363 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6364 self
.assertNotIn('169.254.', output
)
6365 self
.assertNotIn('scope link', output
)
6368 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6369 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)
6370 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6372 output
= check_output('ip -4 address show dev veth99')
6374 self
.assertNotIn('192.168.5.', output
)
6375 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6377 def test_dhcp_client_use_dns(self
):
6378 def check(self
, ipv4
, ipv6
):
6379 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6380 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6381 f
.write('[DHCPv4]\nUseDNS=')
6382 f
.write('yes' if ipv4
else 'no')
6383 f
.write('\n[DHCPv6]\nUseDNS=')
6384 f
.write('yes' if ipv6
else 'no')
6385 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6388 self
.wait_online(['veth99:routable'])
6390 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6391 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6392 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6394 # make resolved re-read the link state file
6395 resolvectl('revert', 'veth99')
6397 output
= resolvectl('dns', 'veth99')
6400 self
.assertIn('192.168.5.1', output
)
6402 self
.assertNotIn('192.168.5.1', output
)
6404 self
.assertIn('2600::1', output
)
6406 self
.assertNotIn('2600::1', output
)
6408 check_json(networkctl_json())
6410 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6413 self
.wait_online(['veth-peer:carrier'])
6414 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6415 '--dhcp-option=option6:dns-server,[2600::1]')
6417 check(self
, True, True)
6418 check(self
, True, False)
6419 check(self
, False, True)
6420 check(self
, False, False)
6422 def test_dhcp_client_use_captive_portal(self
):
6423 def check(self
, ipv4
, ipv6
):
6424 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6425 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6426 f
.write('[DHCPv4]\nUseCaptivePortal=')
6427 f
.write('yes' if ipv4
else 'no')
6428 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6429 f
.write('yes' if ipv6
else 'no')
6430 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6433 self
.wait_online(['veth99:routable'])
6435 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6436 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6437 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6439 output
= networkctl_status('veth99')
6442 self
.assertIn('Captive Portal: http://systemd.io', output
)
6444 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6446 check_json(networkctl_json())
6448 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6451 self
.wait_online(['veth-peer:carrier'])
6452 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6453 '--dhcp-option=option6:103,http://systemd.io')
6455 check(self
, True, True)
6456 check(self
, True, False)
6457 check(self
, False, True)
6458 check(self
, False, False)
6460 def test_dhcp_client_reject_captive_portal(self
):
6461 def check(self
, ipv4
, ipv6
):
6462 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6463 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6464 f
.write('[DHCPv4]\nUseCaptivePortal=')
6465 f
.write('yes' if ipv4
else 'no')
6466 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6467 f
.write('yes' if ipv6
else 'no')
6468 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6471 self
.wait_online(['veth99:routable'])
6473 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6474 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6475 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6477 output
= networkctl_status('veth99')
6479 self
.assertNotIn('Captive Portal: ', output
)
6480 self
.assertNotIn('invalid/url', output
)
6482 check_json(networkctl_json())
6484 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6487 self
.wait_online(['veth-peer:carrier'])
6488 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6489 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6490 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6492 check(self
, True, True)
6493 check(self
, True, False)
6494 check(self
, False, True)
6495 check(self
, False, False)
6497 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6505 def test_dhcp6pd(self
):
6506 def get_dhcp6_prefix(link
):
6507 description
= get_link_description(link
)
6509 self
.assertIn('DHCPv6Client', description
.keys())
6510 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6512 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6516 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6517 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6518 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6519 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6520 '25-dhcp-pd-downstream-dummy97.network',
6521 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6522 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6525 self
.setup_nftset('addr6', 'ipv6_addr')
6526 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6527 self
.setup_nftset('ifindex', 'iface_index')
6530 self
.wait_online(['veth-peer:routable'])
6531 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6532 self
.wait_online(['veth99:degraded'])
6534 # First, test UseAddress=no and Assign=no (issue #29979).
6535 # Note, due to the bug #29701, this test must be done at first.
6536 print('### ip -6 address show dev veth99 scope global')
6537 output
= check_output('ip -6 address show dev veth99 scope global')
6539 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6541 # Check DBus assigned prefix information to veth99
6542 prefixInfo
= get_dhcp6_prefix('veth99')
6544 self
.assertEqual(len(prefixInfo
), 1)
6545 prefixInfo
= prefixInfo
[0]
6547 self
.assertIn('Prefix', prefixInfo
.keys())
6548 self
.assertIn('PrefixLength', prefixInfo
.keys())
6549 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6550 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6552 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6553 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6554 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6555 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6557 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6559 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6560 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6562 print('### ip -6 address show dev veth-peer scope global')
6563 output
= check_output('ip -6 address show dev veth-peer scope global')
6565 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6569 # dummy97: 0x01 (The link will appear later)
6571 # dummy99: auto -> 0x02 (No address assignment)
6576 print('### ip -6 address show dev veth99 scope global')
6577 output
= check_output('ip -6 address show dev veth99 scope global')
6580 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6581 # address in IA_PD (Token=static)
6582 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6583 # address in IA_PD (Token=eui64)
6584 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6585 # address in IA_PD (temporary)
6586 # Note that the temporary addresses may appear after the link enters configured state
6587 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')
6589 print('### ip -6 address show dev test1 scope global')
6590 output
= check_output('ip -6 address show dev test1 scope global')
6592 # address in IA_PD (Token=static)
6593 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6594 # address in IA_PD (temporary)
6595 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')
6597 print('### ip -6 address show dev dummy98 scope global')
6598 output
= check_output('ip -6 address show dev dummy98 scope global')
6600 # address in IA_PD (Token=static)
6601 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6602 # address in IA_PD (temporary)
6603 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')
6605 print('### ip -6 address show dev dummy99 scope global')
6606 output
= check_output('ip -6 address show dev dummy99 scope global')
6609 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6611 print('### ip -6 address show dev veth97 scope global')
6612 output
= check_output('ip -6 address show dev veth97 scope global')
6614 # address in IA_PD (Token=static)
6615 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6616 # address in IA_PD (Token=eui64)
6617 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6618 # address in IA_PD (temporary)
6619 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')
6621 print('### ip -6 address show dev veth97-peer scope global')
6622 output
= check_output('ip -6 address show dev veth97-peer scope global')
6624 # NDisc address (Token=static)
6625 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6626 # NDisc address (Token=eui64)
6627 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6628 # NDisc address (temporary)
6629 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')
6631 print('### ip -6 address show dev veth98 scope global')
6632 output
= check_output('ip -6 address show dev veth98 scope global')
6634 # address in IA_PD (Token=static)
6635 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6636 # address in IA_PD (Token=eui64)
6637 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6638 # address in IA_PD (temporary)
6639 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')
6641 print('### ip -6 address show dev veth98-peer scope global')
6642 output
= check_output('ip -6 address show dev veth98-peer scope global')
6644 # NDisc address (Token=static)
6645 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6646 # NDisc address (Token=eui64)
6647 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6648 # NDisc address (temporary)
6649 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')
6651 print('### ip -6 route show type unreachable')
6652 output
= check_output('ip -6 route show type unreachable')
6654 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6656 print('### ip -6 route show dev veth99')
6657 output
= check_output('ip -6 route show dev veth99')
6659 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6661 print('### ip -6 route show dev test1')
6662 output
= check_output('ip -6 route show dev test1')
6664 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6666 print('### ip -6 route show dev dummy98')
6667 output
= check_output('ip -6 route show dev dummy98')
6669 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6671 print('### ip -6 route show dev dummy99')
6672 output
= check_output('ip -6 route show dev dummy99')
6674 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6676 print('### ip -6 route show dev veth97')
6677 output
= check_output('ip -6 route show dev veth97')
6679 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6681 print('### ip -6 route show dev veth97-peer')
6682 output
= check_output('ip -6 route show dev veth97-peer')
6684 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6686 print('### ip -6 route show dev veth98')
6687 output
= check_output('ip -6 route show dev veth98')
6689 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6691 print('### ip -6 route show dev veth98-peer')
6692 output
= check_output('ip -6 route show dev veth98-peer')
6694 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6696 # Test case for a downstream which appears later
6697 check_output('ip link add dummy97 type dummy')
6698 self
.wait_online(['dummy97:routable'])
6700 print('### ip -6 address show dev dummy97 scope global')
6701 output
= check_output('ip -6 address show dev dummy97 scope global')
6703 # address in IA_PD (Token=static)
6704 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6705 # address in IA_PD (temporary)
6706 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')
6708 print('### ip -6 route show dev dummy97')
6709 output
= check_output('ip -6 route show dev dummy97')
6711 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6713 # Test case for reconfigure
6714 networkctl_reconfigure('dummy98', 'dummy99')
6715 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6717 print('### ip -6 address show dev dummy98 scope global')
6718 output
= check_output('ip -6 address show dev dummy98 scope global')
6720 # address in IA_PD (Token=static)
6721 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6722 # address in IA_PD (temporary)
6723 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')
6725 print('### ip -6 address show dev dummy99 scope global')
6726 output
= check_output('ip -6 address show dev dummy99 scope global')
6729 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6731 print('### ip -6 route show dev dummy98')
6732 output
= check_output('ip -6 route show dev dummy98')
6734 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6736 print('### ip -6 route show dev dummy99')
6737 output
= check_output('ip -6 route show dev dummy99')
6739 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6741 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6743 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6744 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6745 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6746 self
.check_nftset('ifindex', 'dummy98')
6748 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6750 def verify_dhcp4_6rd(self
, tunnel_name
):
6751 print('### ip -4 address show dev veth-peer scope global')
6752 output
= check_output('ip -4 address show dev veth-peer scope global')
6754 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6758 # dummy97: 0x01 (The link will appear later)
6760 # dummy99: auto -> 0x0[23] (No address assignment)
6761 # 6rd-XXX: auto -> 0x0[23]
6766 print('### ip -4 address show dev veth99 scope global')
6767 output
= check_output('ip -4 address show dev veth99 scope global')
6769 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6771 print('### ip -6 address show dev veth99 scope global')
6772 output
= check_output('ip -6 address show dev veth99 scope global')
6774 # address in IA_PD (Token=static)
6775 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6776 # address in IA_PD (Token=eui64)
6777 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6778 # address in IA_PD (temporary)
6779 # Note that the temporary addresses may appear after the link enters configured state
6780 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')
6782 print('### ip -6 address show dev test1 scope global')
6783 output
= check_output('ip -6 address show dev test1 scope global')
6785 # address in IA_PD (Token=static)
6786 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6787 # address in IA_PD (temporary)
6788 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')
6790 print('### ip -6 address show dev dummy98 scope global')
6791 output
= check_output('ip -6 address show dev dummy98 scope global')
6793 # address in IA_PD (Token=static)
6794 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6795 # address in IA_PD (temporary)
6796 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')
6798 print('### ip -6 address show dev dummy99 scope global')
6799 output
= check_output('ip -6 address show dev dummy99 scope global')
6802 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6804 print('### ip -6 address show dev veth97 scope global')
6805 output
= check_output('ip -6 address show dev veth97 scope global')
6807 # address in IA_PD (Token=static)
6808 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6809 # address in IA_PD (Token=eui64)
6810 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6811 # address in IA_PD (temporary)
6812 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')
6814 print('### ip -6 address show dev veth97-peer scope global')
6815 output
= check_output('ip -6 address show dev veth97-peer scope global')
6817 # NDisc address (Token=static)
6818 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6819 # NDisc address (Token=eui64)
6820 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6821 # NDisc address (temporary)
6822 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')
6824 print('### ip -6 address show dev veth98 scope global')
6825 output
= check_output('ip -6 address show dev veth98 scope global')
6827 # address in IA_PD (Token=static)
6828 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6829 # address in IA_PD (Token=eui64)
6830 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6831 # address in IA_PD (temporary)
6832 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')
6834 print('### ip -6 address show dev veth98-peer scope global')
6835 output
= check_output('ip -6 address show dev veth98-peer scope global')
6837 # NDisc address (Token=static)
6838 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6839 # NDisc address (Token=eui64)
6840 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6841 # NDisc address (temporary)
6842 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')
6844 print('### ip -6 route show type unreachable')
6845 output
= check_output('ip -6 route show type unreachable')
6847 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6849 print('### ip -6 route show dev veth99')
6850 output
= check_output('ip -6 route show dev veth99')
6852 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6854 print('### ip -6 route show dev test1')
6855 output
= check_output('ip -6 route show dev test1')
6857 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6859 print('### ip -6 route show dev dummy98')
6860 output
= check_output('ip -6 route show dev dummy98')
6862 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6864 print('### ip -6 route show dev dummy99')
6865 output
= check_output('ip -6 route show dev dummy99')
6867 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6869 print('### ip -6 route show dev veth97')
6870 output
= check_output('ip -6 route show dev veth97')
6872 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6874 print('### ip -6 route show dev veth97-peer')
6875 output
= check_output('ip -6 route show dev veth97-peer')
6877 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6879 print('### ip -6 route show dev veth98')
6880 output
= check_output('ip -6 route show dev veth98')
6882 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6884 print('### ip -6 route show dev veth98-peer')
6885 output
= check_output('ip -6 route show dev veth98-peer')
6887 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6889 print('### ip -6 address show dev dummy97 scope global')
6890 output
= check_output('ip -6 address show dev dummy97 scope global')
6892 # address in IA_PD (Token=static)
6893 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6894 # address in IA_PD (temporary)
6895 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')
6897 print('### ip -6 route show dev dummy97')
6898 output
= check_output('ip -6 route show dev dummy97')
6900 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6902 print(f
'### ip -d link show dev {tunnel_name}')
6903 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6905 self
.assertIn('link/sit 10.100.100.', output
)
6906 self
.assertIn('local 10.100.100.', output
)
6907 self
.assertIn('ttl 64', output
)
6908 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6909 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6911 print(f
'### ip -6 address show dev {tunnel_name}')
6912 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6914 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')
6915 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6917 print(f
'### ip -6 route show dev {tunnel_name}')
6918 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6920 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6921 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6923 print('### ip -6 route show default')
6924 output
= check_output('ip -6 route show default')
6926 self
.assertIn('default', output
)
6927 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6929 def test_dhcp4_6rd(self
):
6930 def get_dhcp_6rd_prefix(link
):
6931 description
= get_link_description(link
)
6933 self
.assertIn('DHCPv4Client', description
.keys())
6934 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6936 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6937 self
.assertIn('Prefix', prefixInfo
.keys())
6938 self
.assertIn('PrefixLength', prefixInfo
.keys())
6939 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6940 self
.assertIn('BorderRouters', prefixInfo
.keys())
6944 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6945 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6946 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6947 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6948 '25-dhcp-pd-downstream-dummy97.network',
6949 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6950 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6951 '80-6rd-tunnel.network')
6954 self
.wait_online(['veth-peer:routable'])
6957 # 6rd-prefix: 2001:db8::/32
6958 # br-addresss: 10.0.0.1
6960 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',
6961 ipv4_range
='10.100.100.100,10.100.100.200',
6962 ipv4_router
='10.0.0.1')
6963 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6964 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6966 # Check the DBus interface for assigned prefix information
6967 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6969 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6970 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6971 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6972 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6974 # Test case for a downstream which appears later
6975 check_output('ip link add dummy97 type dummy')
6976 self
.wait_online(['dummy97:routable'])
6980 for name
in os
.listdir('/sys/class/net/'):
6981 if name
.startswith('6rd-'):
6985 self
.wait_online([f
'{tunnel_name}:routable'])
6987 self
.verify_dhcp4_6rd(tunnel_name
)
6989 # Test case for reconfigure
6990 networkctl_reconfigure('dummy98', 'dummy99')
6991 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6993 self
.verify_dhcp4_6rd(tunnel_name
)
6995 print('Wait for the DHCP lease to be renewed/rebind')
6998 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6999 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
7001 self
.verify_dhcp4_6rd(tunnel_name
)
7003 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7011 def test_ipv6_route_prefix(self
):
7012 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7013 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7016 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
7018 output
= check_output('ip address show dev veth-peer')
7020 self
.assertIn('inet6 2001:db8:0:1:', output
)
7021 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7022 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7024 output
= check_output('ip -6 route show dev veth-peer')
7026 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7027 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7028 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7029 self
.assertIn('2001:db0:fff::/64 via ', output
)
7030 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7031 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7033 output
= check_output('ip address show dev veth99')
7035 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7036 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7037 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7038 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7040 output
= resolvectl('dns', 'veth-peer')
7042 self
.assertRegex(output
, '2001:db8:1:1::2')
7044 output
= resolvectl('domain', 'veth-peer')
7046 self
.assertIn('example.com', output
)
7048 check_json(networkctl_json())
7050 output
= networkctl_json('veth-peer')
7054 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7056 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7057 self
.assertEqual(prefix
, '64:ff9b::')
7059 prefix_length
= pref64
['PrefixLength']
7060 self
.assertEqual(prefix_length
, 96)
7062 def test_ipv6_route_prefix_deny_list(self
):
7063 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7064 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7067 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
7069 output
= check_output('ip address show dev veth-peer')
7071 self
.assertIn('inet6 2001:db8:0:1:', output
)
7072 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7074 output
= check_output('ip -6 route show dev veth-peer')
7076 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7077 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7078 self
.assertIn('2001:db0:fff::/64 via ', output
)
7079 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7081 output
= check_output('ip address show dev veth99')
7083 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7084 self
.assertIn('inet6 2001:db8:0:2:', output
)
7086 output
= resolvectl('dns', 'veth-peer')
7088 self
.assertRegex(output
, '2001:db8:1:1::2')
7090 output
= resolvectl('domain', 'veth-peer')
7092 self
.assertIn('example.com', output
)
7094 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7102 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7108 self
.wait_online(['dummy98:routable'])
7109 self
.check_link_attr('dummy98', 'mtu', mtu
)
7110 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7112 # test normal restart
7114 self
.wait_online(['dummy98:routable'])
7115 self
.check_link_attr('dummy98', 'mtu', mtu
)
7116 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7119 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7121 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7122 ''' test setting mtu/ipv6_mtu with interface already up '''
7125 # note - changing the device mtu resets the ipv6 mtu
7126 check_output('ip link set up mtu 1501 dev dummy98')
7127 check_output('ip link set up mtu 1500 dev dummy98')
7128 self
.check_link_attr('dummy98', 'mtu', '1500')
7129 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7131 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7133 def test_mtu_network(self
):
7134 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7135 self
.check_mtu('1600')
7137 def test_mtu_netdev(self
):
7138 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7139 # note - MTU set by .netdev happens ONLY at device creation!
7140 self
.check_mtu('1600', reset
=False)
7142 def test_mtu_link(self
):
7143 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7144 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7145 self
.check_mtu('1600', reset
=False)
7147 def test_ipv6_mtu(self
):
7148 ''' set ipv6 mtu without setting device mtu '''
7149 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7150 self
.check_mtu('1500', '1400')
7152 def test_ipv6_mtu_toolarge(self
):
7153 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7154 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7155 self
.check_mtu('1500', '1500')
7157 def test_mtu_network_ipv6_mtu(self
):
7158 ''' set ipv6 mtu and set device mtu via network file '''
7159 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7160 self
.check_mtu('1600', '1550')
7162 def test_mtu_netdev_ipv6_mtu(self
):
7163 ''' set ipv6 mtu and set device mtu via netdev file '''
7164 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7165 self
.check_mtu('1600', '1550', reset
=False)
7167 def test_mtu_link_ipv6_mtu(self
):
7168 ''' set ipv6 mtu and set device mtu via link file '''
7169 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7170 self
.check_mtu('1600', '1550', reset
=False)
7173 if __name__
== '__main__':
7174 parser
= argparse
.ArgumentParser()
7175 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7176 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7177 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7178 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7179 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7180 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7181 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7182 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7183 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7184 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7185 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7186 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7187 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7188 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7189 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7190 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7191 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
)
7192 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7195 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7196 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7197 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7198 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7199 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7200 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7201 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7202 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7203 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7204 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7205 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7206 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7207 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7210 networkd_bin
= ns
.networkd_bin
7212 resolved_bin
= ns
.resolved_bin
7213 if ns
.timesyncd_bin
:
7214 timesyncd_bin
= ns
.timesyncd_bin
7216 udevd_bin
= ns
.udevd_bin
7217 if ns
.wait_online_bin
:
7218 wait_online_bin
= ns
.wait_online_bin
7219 if ns
.networkctl_bin
:
7220 networkctl_bin
= ns
.networkctl_bin
7221 if ns
.resolvectl_bin
:
7222 resolvectl_bin
= ns
.resolvectl_bin
7223 if ns
.timedatectl_bin
:
7224 timedatectl_bin
= ns
.timedatectl_bin
7226 udevadm_bin
= ns
.udevadm_bin
7229 systemd_source_dir
= ns
.source_dir
7231 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7232 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7233 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7235 use_valgrind
= ns
.use_valgrind
7236 enable_debug
= ns
.enable_debug
7237 asan_options
= ns
.asan_options
7238 lsan_options
= ns
.lsan_options
7239 ubsan_options
= ns
.ubsan_options
7240 with_coverage
= ns
.with_coverage
7243 # Do not forget the trailing space.
7244 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7246 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7247 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7248 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7249 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7250 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7253 env
.update({'ASAN_OPTIONS': asan_options
})
7255 env
.update({'LSAN_OPTIONS': lsan_options
})
7257 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7259 env
.update({'SYSTEMD_MEMPOOL': '0'})
7261 wait_online_env
= env
.copy()
7263 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7265 sys
.argv
[1:] = unknown_args
7266 unittest
.main(verbosity
=3)