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.
26 network_unit_dir
= '/run/systemd/network'
27 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
28 networkd_ci_temp_dir
= '/run/networkd-ci'
29 udev_rules_dir
= '/run/udev/rules.d'
31 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
32 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
33 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
35 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
36 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
38 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
40 systemd_lib_paths
= ['/usr/lib/systemd', '/lib/systemd']
41 which_paths
= ':'.join(systemd_lib_paths
+ os
.getenv('PATH', os
.defpath
).lstrip(':').split(':'))
42 systemd_source_dir
= None
44 networkd_bin
= shutil
.which('systemd-networkd', path
=which_paths
)
45 resolved_bin
= shutil
.which('systemd-resolved', path
=which_paths
)
46 timesyncd_bin
= shutil
.which('systemd-timesyncd', path
=which_paths
)
47 udevd_bin
= shutil
.which('systemd-udevd', path
=which_paths
)
48 wait_online_bin
= shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
49 networkctl_bin
= shutil
.which('networkctl', path
=which_paths
)
50 resolvectl_bin
= shutil
.which('resolvectl', path
=which_paths
)
51 timedatectl_bin
= shutil
.which('timedatectl', path
=which_paths
)
52 udevadm_bin
= shutil
.which('udevadm', path
=which_paths
)
53 systemd_udev_rules_build_dir
= None
81 saved_ipv4_rules
= None
82 saved_ipv6_rules
= None
86 if os
.path
.exists(path
):
90 shutil
.rmtree(path
, ignore_errors
=True)
96 shutil
.copytree(src
, dst
, copy_function
=shutil
.copy
)
99 os
.makedirs(path
, exist_ok
=True)
102 pathlib
.Path(path
).touch()
104 # pylint: disable=R1710
105 def check_output(*command
, **kwargs
):
106 # This checks the result and returns stdout (and stderr) on success.
107 command
= command
[0].split() + list(command
[1:])
108 ret
= subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
, **kwargs
)
109 if ret
.returncode
== 0:
110 return ret
.stdout
.rstrip()
111 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
113 ret
.check_returncode()
115 def call(*command
, **kwargs
):
116 # This returns returncode. stdout and stderr are merged and shown in console
117 command
= command
[0].split() + list(command
[1:])
118 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).returncode
120 def call_check(*command
, **kwargs
):
121 # Same as call() above, but it triggers CalledProcessError if rc != 0
122 command
= command
[0].split() + list(command
[1:])
123 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).check_returncode()
125 def call_quiet(*command
, **kwargs
):
126 command
= command
[0].split() + list(command
[1:])
127 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
, **kwargs
).returncode
129 def run(*command
, **kwargs
):
130 # This returns CompletedProcess instance.
131 command
= command
[0].split() + list(command
[1:])
132 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, **kwargs
)
134 def check_json(string
):
137 except json
.JSONDecodeError
:
138 print(f
"String is not a valid JSON: '{string}'")
141 def is_module_available(*module_names
):
142 for module_name
in module_names
:
143 lsmod_output
= check_output('lsmod')
144 module_re
= re
.compile(rf
'^{re.escape(module_name)}\b', re
.MULTILINE
)
145 if not module_re
.search(lsmod_output
) and call_quiet('modprobe', module_name
) != 0:
149 def expectedFailureIfModuleIsNotAvailable(*module_names
):
151 return func
if is_module_available(*module_names
) else unittest
.expectedFailure(func
)
155 def expectedFailureIfERSPANv0IsNotSupported():
156 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
158 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')
159 remove_link('erspan99')
160 return func
if rc
== 0 else unittest
.expectedFailure(func
)
164 def expectedFailureIfERSPANv2IsNotSupported():
165 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
167 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')
168 remove_link('erspan99')
169 return func
if rc
== 0 else unittest
.expectedFailure(func
)
173 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
175 rc
= call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
176 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
177 return func
if rc
== 0 else unittest
.expectedFailure(func
)
181 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
183 rc
= call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
184 call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
185 return func
if rc
== 0 else unittest
.expectedFailure(func
)
189 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
192 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
193 ret
= run('ip rule list from 192.168.100.19 table 7')
194 supported
= ret
.returncode
== 0 and 'uidrange 200-300' in ret
.stdout
195 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
196 return func
if supported
else unittest
.expectedFailure(func
)
200 def expectedFailureIfNexthopIsNotAvailable():
202 rc
= call_quiet('ip nexthop list')
203 return func
if rc
== 0 else unittest
.expectedFailure(func
)
207 def expectedFailureIfRTA_VIAIsNotSupported():
209 call_quiet('ip link add dummy98 type dummy')
210 call_quiet('ip link set up dev dummy98')
211 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
212 rc
= call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
213 remove_link('dummy98')
214 return func
if rc
== 0 else unittest
.expectedFailure(func
)
218 def expectedFailureIfAlternativeNameIsNotAvailable():
220 call_quiet('ip link add dummy98 type dummy')
222 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
223 call_quiet('ip link show dev hogehogehogehogehoge') == 0
224 remove_link('dummy98')
225 return func
if supported
else unittest
.expectedFailure(func
)
229 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
231 def finalize(func
, supported
):
232 call_quiet('rmmod netdevsim')
233 return func
if supported
else unittest
.expectedFailure(func
)
235 call_quiet('rmmod netdevsim')
236 if call_quiet('modprobe netdevsim') != 0:
237 return finalize(func
, False)
240 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
243 return finalize(func
, False)
245 return finalize(func
, os
.path
.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
249 # pylint: disable=C0415
250 def compare_kernel_version(min_kernel_version
):
253 from packaging
import version
255 print('Failed to import either platform or packaging module, assuming the comparison failed')
258 # Get only the actual kernel version without any build/distro/arch stuff
259 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
260 kver
= platform
.release().split('-')[0]
261 # Get also rid of '+'
262 kver
= kver
.split('+')[0]
264 return version
.parse(kver
) >= version
.parse(min_kernel_version
)
267 check_output(*udevadm_cmd
, 'control', '--reload')
269 def copy_network_unit(*units
, copy_dropins
=True):
271 Copy networkd unit files into the testbed.
273 Any networkd unit file type can be specified, as well as drop-in files.
275 By default, all drop-ins for a specified unit file are copied in;
276 to avoid that specify dropins=False.
278 When a drop-in file is specified, its unit file is also copied in automatically.
281 mkdir_p(network_unit_dir
)
283 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
284 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
286 if unit
.endswith('.conf'):
288 unit
= os
.path
.dirname(dropin
).rstrip('.d')
289 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
291 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
293 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
295 if unit
.endswith('.link'):
301 def remove_network_unit(*units
):
303 Remove previously copied unit files from the testbed.
305 Drop-ins will be removed automatically.
309 rm_f(os
.path
.join(network_unit_dir
, unit
))
310 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
312 if unit
.endswith('.link') or unit
.endswith('.link.d'):
318 def clear_network_units():
320 if os
.path
.exists(network_unit_dir
):
321 units
= os
.listdir(network_unit_dir
)
323 if unit
.endswith('.link') or unit
.endswith('.link.d'):
326 rm_rf(network_unit_dir
)
331 def copy_networkd_conf_dropin(*dropins
):
332 """Copy networkd.conf dropin files into the testbed."""
333 mkdir_p(networkd_conf_dropin_dir
)
334 for dropin
in dropins
:
335 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
337 def remove_networkd_conf_dropin(*dropins
):
338 """Remove previously copied networkd.conf dropin files from the testbed."""
339 for dropin
in dropins
:
340 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
342 def clear_networkd_conf_dropins():
343 rm_rf(networkd_conf_dropin_dir
)
345 def setup_systemd_udev_rules():
346 if not systemd_udev_rules_build_dir
:
349 mkdir_p(udev_rules_dir
)
351 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
352 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
354 for rule
in os
.listdir(path
):
355 if not rule
.endswith(".rules"):
357 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
359 def copy_udev_rule(*rules
):
360 """Copy udev rules"""
361 mkdir_p(udev_rules_dir
)
363 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
365 def remove_udev_rule(*rules
):
366 """Remove previously copied udev rules"""
368 rm_f(os
.path
.join(udev_rules_dir
, rule
))
370 def clear_udev_rules():
371 rm_rf(udev_rules_dir
)
373 def save_active_units():
374 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
375 'systemd-resolved.service', 'systemd-timesyncd.service',
376 'firewalld.service']:
377 if call(f
'systemctl is-active --quiet {u}') == 0:
378 call(f
'systemctl stop {u}')
379 active_units
.append(u
)
381 def restore_active_units():
382 if 'systemd-networkd.socket' in active_units
:
383 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
384 for u
in active_units
:
385 call(f
'systemctl restart {u}')
387 def create_unit_dropin(unit
, contents
):
388 mkdir_p(f
'/run/systemd/system/{unit}.d')
389 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
390 f
.write('\n'.join(contents
))
392 def create_service_dropin(service
, command
, additional_settings
=None):
396 f
'ExecStart=!!{valgrind_cmd}{command}',
399 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
401 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
403 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
405 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
406 if asan_options
or lsan_options
or ubsan_options
:
407 drop_in
+= ['SystemCallFilter=']
408 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
409 drop_in
+= ['MemoryDenyWriteExecute=no']
412 'Environment=SYSTEMD_MEMPOOL=0',
420 if additional_settings
:
421 drop_in
+= additional_settings
423 create_unit_dropin(f
'{service}.service', drop_in
)
425 def link_exists(link
):
426 return call_quiet(f
'ip link show {link}') == 0
428 def link_resolve(link
):
429 return check_output(f
'ip link show {link}').split(':')[1].strip()
431 def remove_link(*links
, protect
=False):
433 if protect
and link
in protected_links
:
435 if link_exists(link
):
436 call(f
'ip link del dev {link}')
438 def save_existing_links():
439 links
= os
.listdir('/sys/class/net')
441 if link_exists(link
):
442 protected_links
.add(link
)
444 print('### The following links will be protected:')
445 print(', '.join(sorted(list(protected_links
))))
448 links
= os
.listdir('/sys/class/net')
449 remove_link(*links
, protect
=True)
451 def flush_nexthops():
452 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
453 # Hence, we cannot restore nexthops in a simple way.
454 # Let's assume there is no nexthop used in the system
455 call_quiet('ip nexthop flush')
458 # pylint: disable=global-statement
460 saved_routes
= check_output('ip route show table all')
461 print('### The following routes will be protected:')
466 output
= check_output('ip route show table all')
467 for line
in output
.splitlines():
468 if line
in saved_routes
:
470 if 'proto kernel' in line
:
472 if ' dev ' in line
and not ' dev lo ' in line
:
476 print('### Removing routes that did not exist when the test started.')
478 call(f
'ip route del {line}')
480 def save_routing_policy_rules():
481 # pylint: disable=global-statement
482 global saved_ipv4_rules
, saved_ipv6_rules
484 output
= check_output(f
'ip -{ipv} rule show')
485 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
489 saved_ipv4_rules
= save(4)
490 saved_ipv6_rules
= save(6)
492 def flush_routing_policy_rules():
493 def flush(ipv
, saved_rules
):
495 output
= check_output(f
'ip -{ipv} rule show')
496 for line
in output
.splitlines():
497 if line
in saved_rules
:
501 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
503 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
504 priority
= words
[0].rstrip(':')
505 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
507 flush(4, saved_ipv4_rules
)
508 flush(6, saved_ipv6_rules
)
510 def flush_fou_ports():
511 ret
= run('ip fou show')
512 if ret
.returncode
!= 0:
513 return # fou may not be supported
514 for line
in ret
.stdout
.splitlines():
515 port
= line
.split()[1]
516 call(f
'ip fou del port {port}')
518 def flush_l2tp_tunnels():
520 ret
= run('ip l2tp show tunnel')
521 if ret
.returncode
!= 0:
522 return # l2tp may not be supported
523 for line
in ret
.stdout
.splitlines():
525 if words
[0] == 'Tunnel':
526 tid
= words
[1].rstrip(',')
527 call(f
'ip l2tp del tunnel tunnel_id {tid}')
530 # Removing L2TP tunnel is asynchronous and slightly takes a time.
533 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
534 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
538 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
541 # pylint: disable=global-statement
542 global saved_timezone
543 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
544 if r
.returncode
== 0:
545 saved_timezone
= r
.stdout
.rstrip()
546 print(f
'### Saved timezone: {saved_timezone}')
548 def restore_timezone():
550 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
552 def read_link_attr(*args
):
553 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
554 return f
.readline().strip()
556 def read_manager_state_file():
557 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
560 def read_link_state_file(link
):
561 ifindex
= read_link_attr(link
, 'ifindex')
562 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
563 with
open(path
, encoding
='utf-8') as f
:
566 def read_ip_sysctl_attr(link
, attribute
, ipv
):
567 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
568 return f
.readline().strip()
570 def read_ipv6_sysctl_attr(link
, attribute
):
571 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
573 def read_ipv4_sysctl_attr(link
, attribute
):
574 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
576 def stop_by_pid_file(pid_file
):
577 if not os
.path
.exists(pid_file
):
579 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
580 pid
= f
.read().rstrip(' \t\r\n\0')
581 os
.kill(int(pid
), signal
.SIGTERM
)
585 print(f
"PID {pid} is still alive, waiting...")
588 if e
.errno
== errno
.ESRCH
:
590 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
593 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'):
595 ra_mode
= f
',{ra_mode}'
601 f
'--log-facility={dnsmasq_log_file}',
602 '--log-queries=extra',
604 f
'--pid-file={dnsmasq_pid_file}',
605 '--conf-file=/dev/null',
607 f
'--interface={interface}',
608 f
'--dhcp-leasefile={dnsmasq_lease_file}',
610 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
611 f
'--dhcp-range={ipv4_range},2m',
612 '--dhcp-option=option:mtu,1492',
613 f
'--dhcp-option=option:router,{ipv4_router}',
616 ) + additional_options
617 check_output(*command
)
620 stop_by_pid_file(dnsmasq_pid_file
)
621 rm_f(dnsmasq_lease_file
)
622 rm_f(dnsmasq_log_file
)
624 def read_dnsmasq_log_file():
625 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
628 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
629 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
630 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
631 touch(isc_dhcpd_lease_file
)
632 check_output(isc_dhcpd_command
)
634 def stop_isc_dhcpd():
635 stop_by_pid_file(isc_dhcpd_pid_file
)
636 rm_f(isc_dhcpd_lease_file
)
638 def get_dbus_link_path(link
):
639 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
640 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
641 'GetLinkByName', 's', link
])
643 assert out
.startswith(b
'io ')
645 assert out
.endswith(b
'"')
647 return out
[:-1].split('"')[1]
649 def get_dhcp_client_state(link
, family
):
650 link_path
= get_dbus_link_path(link
)
652 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
653 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
654 assert out
.startswith(b
's "')
656 assert out
.endswith(b
'"')
657 return out
[3:-1].decode()
659 def get_dhcp4_client_state(link
):
660 return get_dhcp_client_state(link
, '4')
662 def get_dhcp6_client_state(link
):
663 return get_dhcp_client_state(link
, '6')
665 def get_link_description(link
):
666 link_path
= get_dbus_link_path(link
)
668 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
669 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
670 assert out
.startswith(b
's "')
672 assert out
.endswith(b
'"')
673 json_raw
= out
[2:].decode()
675 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
676 check_json(description
)
677 return json
.loads(description
) # Now parse the json
679 def start_radvd(*additional_options
, config_file
):
680 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
683 f
'--pidfile={radvd_pid_file}',
684 f
'--config={config_file_path}',
685 '--logmethod=stderr',
686 ) + additional_options
687 check_output(*command
)
690 stop_by_pid_file(radvd_pid_file
)
692 def radvd_check_config(config_file
):
693 if not shutil
.which('radvd'):
694 print('radvd is not installed, assuming the config check failed')
697 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
698 # set up (one instance is @unittest.skipX())
699 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
700 return call(f
'radvd --config={config_file_path} --configtest') == 0
702 def networkd_invocation_id():
703 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
705 def read_networkd_log(invocation_id
=None, since
=None):
706 if not invocation_id
:
707 invocation_id
= networkd_invocation_id()
710 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
713 command
.append(f
'--since={since}')
714 return check_output(*command
)
716 def stop_networkd(show_logs
=True):
718 invocation_id
= networkd_invocation_id()
719 check_output('systemctl stop systemd-networkd.socket')
720 check_output('systemctl stop systemd-networkd.service')
722 print(read_networkd_log(invocation_id
))
723 # Check if networkd exits cleanly.
724 assert call_quiet('systemctl is-failed -q systemd-networkd.service') == 1
726 def start_networkd():
727 check_output('systemctl start systemd-networkd')
729 def restart_networkd(show_logs
=True):
731 invocation_id
= networkd_invocation_id()
732 check_output('systemctl restart systemd-networkd.service')
734 print(read_networkd_log(invocation_id
))
737 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
739 def networkctl_reconfigure(*links
):
740 check_output(*networkctl_cmd
, 'reconfigure', *links
, env
=env
)
742 def networkctl_reload(sleep_time
=1):
743 check_output(*networkctl_cmd
, 'reload', env
=env
)
744 # 'networkctl reload' asynchronously reconfigure links.
745 # Hence, we need to wait for a short time for link to be in configuring state.
747 time
.sleep(sleep_time
)
752 def tear_down_common():
753 # 1. stop DHCP/RA servers
759 call_quiet('rmmod netdevsim')
760 call_quiet('rmmod sch_teql')
762 # 3. remove network namespace
763 call_quiet('ip netns del ns99')
773 clear_network_units()
774 clear_networkd_conf_dropins()
779 flush_routing_policy_rules()
783 rm_rf(networkd_ci_temp_dir
)
784 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
786 clear_network_units()
787 clear_networkd_conf_dropins()
790 setup_systemd_udev_rules()
791 copy_udev_rule('00-debug-net.rules')
795 save_existing_links()
797 save_routing_policy_rules()
800 create_service_dropin('systemd-networkd', networkd_bin
,
803 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
805 'StartLimitIntervalSec=0'])
806 create_service_dropin('systemd-resolved', resolved_bin
)
807 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
809 # TODO: also run udevd with sanitizers, valgrind, or coverage
810 #create_service_dropin('systemd-udevd', udevd_bin,
811 # f'{udevadm_bin} control --reload --timeout 0')
813 'systemd-udevd.service',
817 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
821 'systemd-networkd.socket',
824 'StartLimitIntervalSec=0',
828 check_output('systemctl daemon-reload')
829 print(check_output('systemctl cat systemd-networkd.service'))
830 print(check_output('systemctl cat systemd-resolved.service'))
831 print(check_output('systemctl cat systemd-timesyncd.service'))
832 print(check_output('systemctl cat systemd-udevd.service'))
833 check_output('systemctl restart systemd-resolved.service')
834 check_output('systemctl restart systemd-timesyncd.service')
835 check_output('systemctl restart systemd-udevd.service')
837 def tearDownModule():
838 rm_rf(networkd_ci_temp_dir
)
840 clear_network_units()
841 clear_networkd_conf_dropins()
845 rm_rf('/run/systemd/system/systemd-networkd.service.d')
846 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
847 rm_rf('/run/systemd/system/systemd-resolved.service.d')
848 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
849 rm_rf('/run/systemd/system/systemd-udevd.service.d')
850 check_output('systemctl daemon-reload')
851 check_output('systemctl restart systemd-udevd.service')
852 restore_active_units()
855 # pylint: disable=no-member
857 def check_link_exists(self
, link
, expected
=True):
859 self
.assertTrue(link_exists(link
))
861 self
.assertFalse(link_exists(link
))
863 def check_link_attr(self
, *args
):
864 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
866 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
867 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
868 if allow_enoent
and not os
.path
.exists(path
):
870 with
open(path
, encoding
='utf-8') as f
:
871 self
.assertEqual(f
.readline().strip(), expected
)
873 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
874 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
876 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
877 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
879 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
880 def links_exist(*links
):
882 if not link_exists(link
):
886 for iteration
in range(timeout
+ 1):
890 if links_exist(*links
):
893 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
896 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
897 # wait for the interface is activated.
898 invocation_id
= check_output('systemctl show systemd-networkd -p InvocationID --value')
899 needle
= f
'{link}: Bringing link {state}'
901 for iteration
in range(timeout
+ 1):
904 if not link_exists(link
):
906 output
= check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id
)
907 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
910 self
.fail(f
'Timed out waiting for {link} activated.')
913 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
914 """Wait for the link to reach the specified operstate and/or setup state.
916 Specify None or '' for either operstate or setup_state to ignore that state.
917 This will recheck until the state conditions are met or the timeout expires.
919 If the link successfully matches the requested state, this returns True.
920 If this times out waiting for the link to match, the behavior depends on the
921 'fail_assert' parameter; if True, this causes a test assertion failure,
922 otherwise this returns False. The default is to cause assertion failure.
924 Note that this function matches on *exactly* the given operstate and setup_state.
925 To wait for a link to reach *or exceed* a given operstate, use wait_online().
932 for secs
in range(setup_timeout
+ 1):
935 if not link_exists(link
):
937 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', link
, env
=env
)
938 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
942 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
945 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
946 """Wait for the links to reach the specified operstate and/or setup state.
948 This is similar to wait_operstate() but can be used for multiple links,
949 and it also calls systemd-networkd-wait-online to wait for the given operstate.
950 The operstate should be specified in the link name, like 'eth0:degraded'.
951 If just a link name is provided, wait-online's default operstate to wait for is degraded.
953 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
954 'setup_timeout' controls the per-link timeout waiting for the setup_state.
956 Set 'bool_any' to True to wait for any (instead of all) of the given links.
957 If this is set, no setup_state checks are done.
959 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
960 This is applied only for the operational state 'degraded' or above.
962 Note that this function waits for the links to reach *or exceed* the given operstate.
963 However, the setup_state, if specified, must be matched *exactly*.
965 This returns if the links reached the requested operstate/setup_state; otherwise it
966 raises CalledProcessError or fails test assertion.
968 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
976 check_output(*args
, env
=wait_online_env
)
977 except subprocess
.CalledProcessError
:
978 # show detailed status on failure
979 for link
in links_with_operstate
:
980 name
= link
.split(':')[0]
981 if link_exists(name
):
982 call(*networkctl_cmd
, '-n', '0', 'status', name
, env
=env
)
984 if not bool_any
and setup_state
:
985 for link
in links_with_operstate
:
986 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
988 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
989 for i
in range(timeout_sec
):
992 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
993 if re
.search(address_regex
, output
) and 'tentative' not in output
:
996 self
.assertRegex(output
, address_regex
)
998 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
999 for i
in range(timeout_sec
):
1002 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1003 if not re
.search(address_regex
, output
):
1006 self
.assertNotRegex(output
, address_regex
)
1008 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1009 for i
in range(timeout_sec
):
1012 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1013 if re
.search(route_regex
, output
):
1016 self
.assertRegex(output
, route_regex
)
1018 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1019 if not shutil
.which('selinuxenabled'):
1020 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1021 elif call_quiet('selinuxenabled') != 0:
1022 print('## Checking NetLabel skipped: SELinux disabled.')
1023 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1024 print('## Checking NetLabel skipped: netlabelctl command not found.')
1026 output
= check_output('netlabelctl unlbl list')
1028 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1030 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1031 if not shutil
.which('nft'):
1032 print('## Setting up NFT sets skipped: nft command not found.')
1034 if call(f
'nft add table inet sd_test') != 0:
1035 print('## Setting up NFT table failed.')
1037 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1038 print('## Setting up NFT sets failed.')
1041 def teardown_nftset(self
, *filters
):
1042 if not shutil
.which('nft'):
1043 print('## Tearing down NFT sets skipped: nft command not found.')
1045 for filter_name
in filters
:
1046 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1047 print('## Tearing down NFT sets failed.')
1049 if call(f
'nft delete table inet sd_test') != 0:
1050 print('## Tearing down NFT table failed.')
1053 def check_nftset(self
, filter_name
, contents
):
1054 if not shutil
.which('nft'):
1055 print('## Checking NFT sets skipped: nft command not found.')
1057 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1059 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1061 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1069 @expectedFailureIfAlternativeNameIsNotAvailable()
1070 def test_altname(self
):
1071 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1073 self
.wait_online(['dummy98:degraded'])
1075 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1076 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1078 @expectedFailureIfAlternativeNameIsNotAvailable()
1079 def test_rename_to_altname(self
):
1080 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1081 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1083 self
.wait_online(['dummyalt:degraded'])
1085 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummyalt', env
=env
)
1086 self
.assertIn('hogehogehogehogehogehoge', output
)
1087 self
.assertNotIn('dummy98', output
)
1089 def test_reconfigure(self
):
1090 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1092 self
.wait_online(['dummy98:routable'])
1094 output
= check_output('ip -4 address show dev dummy98')
1096 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1097 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1098 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1100 check_output('ip address del 10.1.2.3/16 dev dummy98')
1101 check_output('ip address del 10.1.2.4/16 dev dummy98')
1102 check_output('ip address del 10.2.2.4/16 dev dummy98')
1104 networkctl_reconfigure('dummy98')
1105 self
.wait_online(['dummy98:routable'])
1107 output
= check_output('ip -4 address show dev dummy98')
1109 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1110 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1111 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1113 remove_network_unit('25-address-static.network')
1116 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1118 output
= check_output('ip -4 address show dev dummy98')
1120 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1121 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1122 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1124 copy_network_unit('25-address-static.network', copy_dropins
=False)
1126 self
.wait_online(['dummy98:routable'])
1128 output
= check_output('ip -4 address show dev dummy98')
1130 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1131 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1132 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1134 def test_renew(self
):
1136 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
1137 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
1139 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1140 self
.assertIn('Gateway: 192.168.5.3', output
)
1141 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1142 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1144 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1147 output
= check_output(*networkctl_cmd
, '--lines=0', '--stats', '--all', '--full', '--json=short', 'status')
1150 for verb
in ['renew', 'forcerenew']:
1151 call_check(*networkctl_cmd
, verb
, 'veth99')
1153 call_check(*networkctl_cmd
, verb
, 'veth99', 'veth99', 'veth99')
1156 def test_up_down(self
):
1157 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1159 self
.wait_online(['dummy98:routable'])
1161 call_check(*networkctl_cmd
, 'down', 'dummy98')
1162 self
.wait_online(['dummy98:off'])
1163 call_check(*networkctl_cmd
, 'up', 'dummy98')
1164 self
.wait_online(['dummy98:routable'])
1165 call_check(*networkctl_cmd
, 'down', 'dummy98', 'dummy98', 'dummy98')
1166 self
.wait_online(['dummy98:off'])
1167 call_check(*networkctl_cmd
, 'up', 'dummy98', 'dummy98', 'dummy98')
1168 self
.wait_online(['dummy98:routable'])
1170 def test_reload(self
):
1173 copy_network_unit('11-dummy.netdev')
1175 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1177 copy_network_unit('11-dummy.network')
1179 self
.wait_online(['test1:degraded'])
1181 remove_network_unit('11-dummy.network')
1183 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1185 remove_network_unit('11-dummy.netdev')
1187 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1189 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1191 self
.wait_operstate('test1', 'degraded')
1193 def test_glob(self
):
1194 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1197 self
.wait_online(['test1:degraded'])
1199 output
= check_output(*networkctl_cmd
, 'list', env
=env
)
1200 self
.assertRegex(output
, '1 lo ')
1201 self
.assertRegex(output
, 'test1')
1203 output
= check_output(*networkctl_cmd
, 'list', 'test1', env
=env
)
1204 self
.assertNotRegex(output
, '1 lo ')
1205 self
.assertRegex(output
, 'test1')
1207 output
= check_output(*networkctl_cmd
, 'list', 'te*', env
=env
)
1208 self
.assertNotRegex(output
, '1 lo ')
1209 self
.assertRegex(output
, 'test1')
1211 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'te*', env
=env
)
1212 self
.assertNotRegex(output
, '1: lo ')
1213 self
.assertRegex(output
, 'test1')
1215 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'tes[a-z][0-9]', env
=env
)
1216 self
.assertNotRegex(output
, '1: lo ')
1217 self
.assertRegex(output
, 'test1')
1220 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1223 self
.wait_online(['test1:degraded'])
1225 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1226 self
.assertRegex(output
, 'MTU: 1600')
1228 def test_type(self
):
1229 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1231 self
.wait_online(['test1:degraded'])
1233 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1235 self
.assertRegex(output
, 'Type: ether')
1237 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1239 self
.assertRegex(output
, 'Type: loopback')
1241 def test_unit_file(self
):
1242 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1244 self
.wait_online(['test1:degraded'])
1246 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1248 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1249 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1250 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1251 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1253 output
= read_networkd_log()
1254 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
)
1256 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1257 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1258 # Let's reprocess the interface and drop the property.
1259 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1260 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1262 self
.assertIn('Link File: n/a', output
)
1263 self
.assertIn('Network File: n/a', output
)
1265 def test_delete_links(self
):
1266 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1267 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1270 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
1272 check_output(*networkctl_cmd
, 'delete', 'test1', 'veth99', env
=env
)
1273 self
.check_link_exists('test1', expected
=False)
1274 self
.check_link_exists('veth99', expected
=False)
1275 self
.check_link_exists('veth-peer', expected
=False)
1277 def test_label(self
):
1278 call_check(*networkctl_cmd
, 'label')
1280 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1288 @expectedFailureIfAlternativeNameIsNotAvailable()
1289 def test_match(self
):
1290 copy_network_unit('12-dummy-mac.netdev',
1291 '12-dummy-match-mac-01.network',
1292 '12-dummy-match-mac-02.network',
1293 '12-dummy-match-renamed.network',
1294 '12-dummy-match-altname.network',
1295 '12-dummy-altname.link')
1298 self
.wait_online(['dummy98:routable'])
1299 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1300 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1301 output
= check_output('ip -4 address show dev dummy98')
1302 self
.assertIn('10.0.0.1/16', output
)
1304 check_output('ip link set dev dummy98 down')
1305 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1307 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1308 self
.wait_online(['dummy98:routable'])
1309 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1310 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1312 check_output('ip link set dev dummy98 down')
1313 check_output('ip link set dev dummy98 name dummy98-1')
1315 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1316 self
.wait_online(['dummy98-1:routable'])
1317 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-1', env
=env
)
1318 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1320 check_output('ip link set dev dummy98-1 down')
1321 check_output('ip link set dev dummy98-1 name dummy98-2')
1322 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1324 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1325 self
.wait_online(['dummy98-2:routable'])
1326 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-2', env
=env
)
1327 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1329 def test_match_udev_property(self
):
1330 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1332 self
.wait_online(['dummy98:routable'])
1334 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1336 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1338 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1346 def test_wait_online_any(self
):
1347 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1350 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
1352 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1353 self
.wait_operstate('test1', 'degraded')
1355 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1363 def test_dropin_and_name_conflict(self
):
1364 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1367 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
1369 output
= check_output('ip link show dropin-test')
1371 self
.assertRegex(output
, '00:50:56:c0:00:28')
1373 @expectedFailureIfModuleIsNotAvailable('bareudp')
1374 def test_bareudp(self
):
1375 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1378 self
.wait_online(['bareudp99:degraded'])
1380 output
= check_output('ip -d link show bareudp99')
1382 self
.assertRegex(output
, 'dstport 1000 ')
1383 self
.assertRegex(output
, 'ethertype ip ')
1385 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1386 def test_batadv(self
):
1387 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1390 self
.wait_online(['batadv99:degraded'])
1392 output
= check_output('ip -d link show batadv99')
1394 self
.assertRegex(output
, 'batadv')
1396 def test_bridge(self
):
1397 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1400 self
.wait_online(['bridge99:no-carrier'])
1402 tick
= os
.sysconf('SC_CLK_TCK')
1403 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1404 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1405 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1406 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1407 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1408 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1409 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1410 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1411 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1413 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
1415 self
.assertRegex(output
, 'Priority: 9')
1416 self
.assertRegex(output
, 'STP: yes')
1417 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1419 output
= check_output('ip -d link show bridge99')
1421 self
.assertIn('vlan_filtering 1 ', output
)
1422 self
.assertIn('vlan_protocol 802.1ad ', output
)
1423 self
.assertIn('vlan_default_pvid 9 ', output
)
1425 def test_bond(self
):
1426 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1429 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1431 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1432 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1433 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1434 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1435 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1436 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1437 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1438 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1439 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1440 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1441 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1443 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1444 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1446 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond99', env
=env
)
1448 self
.assertIn('Mode: 802.3ad', output
)
1449 self
.assertIn('Miimon: 1s', output
)
1450 self
.assertIn('Updelay: 2s', output
)
1451 self
.assertIn('Downdelay: 2s', output
)
1453 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond98', env
=env
)
1455 self
.assertIn('Mode: balance-tlb', output
)
1457 def test_vlan(self
):
1458 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1459 '21-vlan.network', '21-vlan-test1.network')
1462 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1464 output
= check_output('ip -d link show test1')
1466 self
.assertRegex(output
, ' mtu 2000 ')
1468 output
= check_output('ip -d link show vlan99')
1470 self
.assertIn(' mtu 2000 ', output
)
1471 self
.assertIn('REORDER_HDR', output
)
1472 self
.assertIn('LOOSE_BINDING', output
)
1473 self
.assertIn('GVRP', output
)
1474 self
.assertIn('MVRP', output
)
1475 self
.assertIn(' id 99 ', output
)
1476 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1477 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1479 output
= check_output('ip -4 address show dev test1')
1481 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1482 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1484 output
= check_output('ip -4 address show dev vlan99')
1486 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1488 def test_vlan_on_bond(self
):
1489 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1490 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1492 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1493 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1495 self
.wait_online(['bond99:off'])
1496 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1498 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1499 # that the issue is fixed by the commit, let's allow to match both string.
1500 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1504 if log_re
.search(read_networkd_log()):
1509 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1511 self
.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
1513 def test_macvtap(self
):
1515 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1521 print(f
'### test_macvtap(mode={mode})')
1522 with self
.subTest(mode
=mode
):
1523 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1524 '11-dummy.netdev', '25-macvtap.network')
1525 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1526 f
.write('[MACVTAP]\nMode=' + mode
)
1529 self
.wait_online(['macvtap99:degraded',
1530 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1532 output
= check_output('ip -d link show macvtap99')
1534 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1536 def test_macvlan(self
):
1538 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1544 print(f
'### test_macvlan(mode={mode})')
1545 with self
.subTest(mode
=mode
):
1546 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1547 '11-dummy.netdev', '25-macvlan.network')
1548 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1549 f
.write('[MACVLAN]\nMode=' + mode
)
1552 self
.wait_online(['macvlan99:degraded',
1553 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1555 output
= check_output('ip -d link show test1')
1557 self
.assertRegex(output
, ' mtu 2000 ')
1559 output
= check_output('ip -d link show macvlan99')
1561 self
.assertRegex(output
, ' mtu 2000 ')
1562 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1564 remove_link('test1')
1567 check_output("ip link add test1 type dummy")
1568 self
.wait_online(['macvlan99:degraded',
1569 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1571 output
= check_output('ip -d link show test1')
1573 self
.assertRegex(output
, ' mtu 2000 ')
1575 output
= check_output('ip -d link show macvlan99')
1577 self
.assertRegex(output
, ' mtu 2000 ')
1578 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1580 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1581 def test_ipvlan(self
):
1583 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1589 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1590 with self
.subTest(mode
=mode
, flag
=flag
):
1591 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1592 '11-dummy.netdev', '25-ipvlan.network')
1593 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1594 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1597 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1599 output
= check_output('ip -d link show ipvlan99')
1601 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1603 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1604 def test_ipvtap(self
):
1606 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1612 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1613 with self
.subTest(mode
=mode
, flag
=flag
):
1614 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1615 '11-dummy.netdev', '25-ipvtap.network')
1616 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1617 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1620 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1622 output
= check_output('ip -d link show ipvtap99')
1624 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1626 def test_veth(self
):
1627 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1628 '25-veth-mtu.netdev')
1631 self
.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1633 output
= check_output('ip -d link show veth99')
1635 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1636 output
= check_output('ip -d link show veth-peer')
1638 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1640 output
= check_output('ip -d link show veth-mtu')
1642 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1643 self
.assertRegex(output
, 'mtu 1800')
1644 output
= check_output('ip -d link show veth-mtu-peer')
1646 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1647 self
.assertRegex(output
, 'mtu 1800')
1649 def test_tuntap(self
):
1650 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1653 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'])
1655 pid
= networkd_pid()
1656 name
= psutil
.Process(pid
).name()[:15]
1658 output
= check_output('ip -d tuntap show')
1660 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1661 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1663 output
= check_output('ip -d link show testtun99')
1665 # Old ip command does not support IFF_ flags
1666 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1667 self
.assertIn('UP,LOWER_UP', output
)
1669 output
= check_output('ip -d link show testtap99')
1671 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1672 self
.assertIn('UP,LOWER_UP', output
)
1674 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1677 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state
='unmanaged')
1679 pid
= networkd_pid()
1680 name
= psutil
.Process(pid
).name()[:15]
1682 output
= check_output('ip -d tuntap show')
1684 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1685 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1687 output
= check_output('ip -d link show testtun99')
1689 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1690 self
.assertIn('UP,LOWER_UP', output
)
1692 output
= check_output('ip -d link show testtap99')
1694 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1695 self
.assertIn('UP,LOWER_UP', output
)
1697 clear_network_units()
1699 self
.wait_online(['testtun99:off', 'testtap99:off'], setup_state
='unmanaged')
1701 output
= check_output('ip -d tuntap show')
1703 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1704 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1709 output
= check_output('ip -d link show testtun99')
1711 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1712 if 'NO-CARRIER' in output
:
1720 output
= check_output('ip -d link show testtap99')
1722 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1723 if 'NO-CARRIER' in output
:
1728 @expectedFailureIfModuleIsNotAvailable('vrf')
1730 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1733 self
.wait_online(['vrf99:carrier'])
1735 @expectedFailureIfModuleIsNotAvailable('vcan')
1736 def test_vcan(self
):
1737 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1738 '25-vcan98.netdev', '25-vcan98.network')
1741 self
.wait_online(['vcan99:carrier', 'vcan98:carrier'])
1743 # https://github.com/systemd/systemd/issues/30140
1744 output
= check_output('ip -d link show vcan99')
1746 self
.assertIn('mtu 16 ', output
)
1748 output
= check_output('ip -d link show vcan98')
1750 self
.assertIn('mtu 16 ', output
)
1752 @expectedFailureIfModuleIsNotAvailable('vxcan')
1753 def test_vxcan(self
):
1754 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1757 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1759 @expectedFailureIfModuleIsNotAvailable('wireguard')
1760 def test_wireguard(self
):
1761 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1762 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1763 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1764 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1766 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1768 output
= check_output('ip -4 address show dev wg99')
1770 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1772 output
= check_output('ip -4 address show dev wg99')
1774 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1776 output
= check_output('ip -6 address show dev wg99')
1778 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1780 output
= check_output('ip -4 address show dev wg98')
1782 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1784 output
= check_output('ip -6 address show dev wg98')
1786 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1788 output
= check_output('ip -4 route show dev wg99 table 1234')
1790 self
.assertIn('192.168.26.0/24 proto static metric 123', output
)
1792 output
= check_output('ip -6 route show dev wg99 table 1234')
1794 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1796 output
= check_output('ip -6 route show dev wg98 table 1234')
1798 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1799 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1800 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1801 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1802 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1803 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1804 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1805 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1806 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1807 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1808 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1809 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1810 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1811 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1812 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1813 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1814 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1815 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1816 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1817 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1818 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1819 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1820 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1821 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1822 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1823 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1824 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1825 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1826 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1827 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1828 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1829 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1830 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1831 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1832 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1833 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1834 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1835 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1836 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1837 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1838 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1839 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1840 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1841 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1842 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1843 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1845 if shutil
.which('wg'):
1848 output
= check_output('wg show wg99 listen-port')
1849 self
.assertEqual(output
, '51820')
1850 output
= check_output('wg show wg99 fwmark')
1851 self
.assertEqual(output
, '0x4d2')
1852 output
= check_output('wg show wg99 private-key')
1853 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1854 output
= check_output('wg show wg99 allowed-ips')
1855 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1856 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1857 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1858 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1859 output
= check_output('wg show wg99 persistent-keepalive')
1860 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1861 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1862 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1863 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1864 output
= check_output('wg show wg99 endpoints')
1865 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1866 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1867 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1868 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1869 output
= check_output('wg show wg99 preshared-keys')
1870 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1871 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1872 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1873 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1875 output
= check_output('wg show wg98 private-key')
1876 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1878 output
= check_output('wg show wg97 listen-port')
1879 self
.assertEqual(output
, '51821')
1880 output
= check_output('wg show wg97 fwmark')
1881 self
.assertEqual(output
, '0x4d3')
1883 def test_geneve(self
):
1884 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1887 self
.wait_online(['geneve99:degraded'])
1889 output
= check_output('ip -d link show geneve99')
1891 self
.assertRegex(output
, '192.168.22.1')
1892 self
.assertRegex(output
, '6082')
1893 self
.assertRegex(output
, 'udpcsum')
1894 self
.assertRegex(output
, 'udp6zerocsumrx')
1896 def test_ipip_tunnel(self
):
1897 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1898 '25-ipip-tunnel.netdev', '25-tunnel.network',
1899 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1900 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1901 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1903 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1905 output
= check_output('ip -d link show ipiptun99')
1907 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1908 output
= check_output('ip -d link show ipiptun98')
1910 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1911 output
= check_output('ip -d link show ipiptun97')
1913 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1914 output
= check_output('ip -d link show ipiptun96')
1916 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1918 def test_gre_tunnel(self
):
1919 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1920 '25-gre-tunnel.netdev', '25-tunnel.network',
1921 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1922 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1923 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1925 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1927 output
= check_output('ip -d link show gretun99')
1929 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1930 self
.assertRegex(output
, 'ikey 1.2.3.103')
1931 self
.assertRegex(output
, 'okey 1.2.4.103')
1932 self
.assertRegex(output
, 'iseq')
1933 self
.assertRegex(output
, 'oseq')
1934 output
= check_output('ip -d link show gretun98')
1936 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1937 self
.assertRegex(output
, 'ikey 0.0.0.104')
1938 self
.assertRegex(output
, 'okey 0.0.0.104')
1939 self
.assertNotRegex(output
, 'iseq')
1940 self
.assertNotRegex(output
, 'oseq')
1941 output
= check_output('ip -d link show gretun97')
1943 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1944 self
.assertRegex(output
, 'ikey 0.0.0.105')
1945 self
.assertRegex(output
, 'okey 0.0.0.105')
1946 self
.assertNotRegex(output
, 'iseq')
1947 self
.assertNotRegex(output
, 'oseq')
1948 output
= check_output('ip -d link show gretun96')
1950 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1951 self
.assertRegex(output
, 'ikey 0.0.0.106')
1952 self
.assertRegex(output
, 'okey 0.0.0.106')
1953 self
.assertNotRegex(output
, 'iseq')
1954 self
.assertNotRegex(output
, 'oseq')
1956 def test_ip6gre_tunnel(self
):
1957 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
1958 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1959 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1960 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1961 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1964 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1966 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
1968 output
= check_output('ip -d link show ip6gretun99')
1970 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1971 output
= check_output('ip -d link show ip6gretun98')
1973 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1974 output
= check_output('ip -d link show ip6gretun97')
1976 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1977 output
= check_output('ip -d link show ip6gretun96')
1979 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
1981 def test_gretap_tunnel(self
):
1982 copy_network_unit('12-dummy.netdev', '25-gretap.network',
1983 '25-gretap-tunnel.netdev', '25-tunnel.network',
1984 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1986 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1988 output
= check_output('ip -d link show gretap99')
1990 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1991 self
.assertRegex(output
, 'ikey 0.0.0.106')
1992 self
.assertRegex(output
, 'okey 0.0.0.106')
1993 self
.assertRegex(output
, 'iseq')
1994 self
.assertRegex(output
, 'oseq')
1995 self
.assertIn('nopmtudisc', output
)
1996 self
.assertIn('ignore-df', output
)
1997 output
= check_output('ip -d link show gretap98')
1999 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2000 self
.assertRegex(output
, 'ikey 0.0.0.107')
2001 self
.assertRegex(output
, 'okey 0.0.0.107')
2002 self
.assertRegex(output
, 'iseq')
2003 self
.assertRegex(output
, 'oseq')
2005 def test_ip6gretap_tunnel(self
):
2006 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2007 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2008 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2010 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
2012 output
= check_output('ip -d link show ip6gretap99')
2014 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2015 output
= check_output('ip -d link show ip6gretap98')
2017 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2019 def test_vti_tunnel(self
):
2020 copy_network_unit('12-dummy.netdev', '25-vti.network',
2021 '25-vti-tunnel.netdev', '25-tunnel.network',
2022 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2023 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2024 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2026 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2028 output
= check_output('ip -d link show vtitun99')
2030 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2031 output
= check_output('ip -d link show vtitun98')
2033 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2034 output
= check_output('ip -d link show vtitun97')
2036 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2037 output
= check_output('ip -d link show vtitun96')
2039 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2041 def test_vti6_tunnel(self
):
2042 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2043 '25-vti6-tunnel.netdev', '25-tunnel.network',
2044 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2045 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2047 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2049 output
= check_output('ip -d link show vti6tun99')
2051 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2052 output
= check_output('ip -d link show vti6tun98')
2054 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2055 output
= check_output('ip -d link show vti6tun97')
2057 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2059 def test_ip6tnl_tunnel(self
):
2060 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2061 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2062 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2063 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2064 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2065 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2066 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2068 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2069 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2070 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2072 output
= check_output('ip -d link show ip6tnl99')
2074 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2075 output
= check_output('ip -d link show ip6tnl98')
2077 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2078 output
= check_output('ip -d link show ip6tnl97')
2080 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2081 output
= check_output('ip -d link show ip6tnl-external')
2083 self
.assertIn('ip6tnl-external@NONE:', output
)
2084 self
.assertIn('ip6tnl external ', output
)
2085 output
= check_output('ip -d link show ip6tnl-slaac')
2087 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2089 output
= check_output('ip -6 address show veth99')
2091 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2093 output
= check_output('ip -4 route show default')
2095 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2097 def test_sit_tunnel(self
):
2098 copy_network_unit('12-dummy.netdev', '25-sit.network',
2099 '25-sit-tunnel.netdev', '25-tunnel.network',
2100 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2101 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2102 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2104 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2106 output
= check_output('ip -d link show sittun99')
2108 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2109 output
= check_output('ip -d link show sittun98')
2111 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2112 output
= check_output('ip -d link show sittun97')
2114 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2115 output
= check_output('ip -d link show sittun96')
2117 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2119 def test_isatap_tunnel(self
):
2120 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2121 '25-isatap-tunnel.netdev', '25-tunnel.network')
2123 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2125 output
= check_output('ip -d link show isataptun99')
2127 self
.assertRegex(output
, "isatap ")
2129 def test_6rd_tunnel(self
):
2130 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2131 '25-6rd-tunnel.netdev', '25-tunnel.network')
2133 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2135 output
= check_output('ip -d link show sittun99')
2137 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2139 @expectedFailureIfERSPANv0IsNotSupported()
2140 def test_erspan_tunnel_v0(self
):
2141 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2142 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2143 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2145 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2147 output
= check_output('ip -d link show erspan99')
2149 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2150 self
.assertIn('erspan_ver 0', output
)
2151 self
.assertNotIn('erspan_index 123', output
)
2152 self
.assertNotIn('erspan_dir ingress', output
)
2153 self
.assertNotIn('erspan_hwid 1f', output
)
2154 self
.assertIn('ikey 0.0.0.101', output
)
2155 self
.assertIn('iseq', output
)
2156 self
.assertIn('nopmtudisc', output
)
2157 self
.assertIn('ignore-df', output
)
2158 output
= check_output('ip -d link show erspan98')
2160 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2161 self
.assertIn('erspan_ver 0', output
)
2162 self
.assertNotIn('erspan_index 124', output
)
2163 self
.assertNotIn('erspan_dir egress', output
)
2164 self
.assertNotIn('erspan_hwid 2f', output
)
2165 self
.assertIn('ikey 0.0.0.102', output
)
2166 self
.assertIn('iseq', output
)
2168 def test_erspan_tunnel_v1(self
):
2169 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2170 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2171 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2173 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2175 output
= check_output('ip -d link show erspan99')
2177 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2178 self
.assertIn('erspan_ver 1', output
)
2179 self
.assertIn('erspan_index 123', output
)
2180 self
.assertNotIn('erspan_dir ingress', output
)
2181 self
.assertNotIn('erspan_hwid 1f', output
)
2182 self
.assertIn('ikey 0.0.0.101', output
)
2183 self
.assertIn('okey 0.0.0.101', output
)
2184 self
.assertIn('iseq', output
)
2185 self
.assertIn('oseq', output
)
2186 output
= check_output('ip -d link show erspan98')
2188 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2189 self
.assertIn('erspan_ver 1', output
)
2190 self
.assertIn('erspan_index 124', output
)
2191 self
.assertNotIn('erspan_dir egress', output
)
2192 self
.assertNotIn('erspan_hwid 2f', output
)
2193 self
.assertIn('ikey 0.0.0.102', output
)
2194 self
.assertIn('okey 0.0.0.102', output
)
2195 self
.assertIn('iseq', output
)
2196 self
.assertIn('oseq', output
)
2198 @expectedFailureIfERSPANv2IsNotSupported()
2199 def test_erspan_tunnel_v2(self
):
2200 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2201 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2202 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2204 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2206 output
= check_output('ip -d link show erspan99')
2208 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2209 self
.assertIn('erspan_ver 2', output
)
2210 self
.assertNotIn('erspan_index 123', output
)
2211 self
.assertIn('erspan_dir ingress', output
)
2212 self
.assertIn('erspan_hwid 0x1f', output
)
2213 self
.assertIn('ikey 0.0.0.101', output
)
2214 self
.assertIn('okey 0.0.0.101', output
)
2215 self
.assertIn('iseq', output
)
2216 self
.assertIn('oseq', output
)
2217 output
= check_output('ip -d link show erspan98')
2219 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2220 self
.assertIn('erspan_ver 2', output
)
2221 self
.assertNotIn('erspan_index 124', output
)
2222 self
.assertIn('erspan_dir egress', output
)
2223 self
.assertIn('erspan_hwid 0x2f', output
)
2224 self
.assertIn('ikey 0.0.0.102', output
)
2225 self
.assertIn('okey 0.0.0.102', output
)
2226 self
.assertIn('iseq', output
)
2227 self
.assertIn('oseq', output
)
2229 def test_tunnel_independent(self
):
2230 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2233 self
.wait_online(['ipiptun99:carrier'])
2235 def test_tunnel_independent_loopback(self
):
2236 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2239 self
.wait_online(['ipiptun99:carrier'])
2241 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2242 def test_xfrm(self
):
2243 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2244 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2245 '26-netdev-link-local-addressing-yes.network')
2248 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2250 output
= check_output('ip -d link show dev xfrm98')
2252 self
.assertIn('xfrm98@dummy98:', output
)
2253 self
.assertIn('xfrm if_id 0x98 ', output
)
2255 output
= check_output('ip -d link show dev xfrm99')
2257 self
.assertIn('xfrm99@lo:', output
)
2258 self
.assertIn('xfrm if_id 0x99 ', output
)
2260 @expectedFailureIfModuleIsNotAvailable('fou')
2262 # The following redundant check is necessary for CentOS CI.
2263 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2264 self
.assertTrue(is_module_available('fou'))
2266 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2267 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2268 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2271 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2273 output
= check_output('ip fou show')
2275 self
.assertRegex(output
, 'port 55555 ipproto 4')
2276 self
.assertRegex(output
, 'port 55556 ipproto 47')
2278 output
= check_output('ip -d link show ipiptun96')
2280 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2281 output
= check_output('ip -d link show sittun96')
2283 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2284 output
= check_output('ip -d link show gretun96')
2286 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2287 output
= check_output('ip -d link show gretap96')
2289 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2291 def test_vxlan(self
):
2292 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2293 '25-vxlan.netdev', '25-vxlan.network',
2294 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2295 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2296 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2297 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2300 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2301 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2303 output
= check_output('ip -d -d link show vxlan99')
2305 self
.assertIn('999', output
)
2306 self
.assertIn('5555', output
)
2307 self
.assertIn('l2miss', output
)
2308 self
.assertIn('l3miss', output
)
2309 self
.assertIn('gbp', output
)
2310 # Since [0] some of the options use slightly different names and some
2311 # options with default values are shown only if the -d(etails) setting
2313 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2314 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2315 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2316 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2317 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2318 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2320 output
= check_output('bridge fdb show dev vxlan99')
2322 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2323 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2324 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2326 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'vxlan99', env
=env
)
2328 self
.assertIn('VNI: 999', output
)
2329 self
.assertIn('Destination Port: 5555', output
)
2330 self
.assertIn('Underlying Device: test1', output
)
2332 output
= check_output('bridge fdb show dev vxlan97')
2334 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2335 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2336 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2338 output
= check_output('ip -d link show vxlan-slaac')
2340 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2342 output
= check_output('ip -6 address show veth99')
2344 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2346 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2347 def test_macsec(self
):
2348 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2349 '26-macsec.network', '12-dummy.netdev')
2352 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2354 output
= check_output('ip -d link show macsec99')
2356 self
.assertRegex(output
, 'macsec99@dummy98')
2357 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2358 self
.assertRegex(output
, 'encrypt on')
2360 output
= check_output('ip macsec show macsec99')
2362 self
.assertRegex(output
, 'encrypt on')
2363 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2364 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2365 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2366 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2367 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2368 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2369 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2370 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2371 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2372 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2373 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2375 def test_nlmon(self
):
2376 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2379 self
.wait_online(['nlmon99:carrier'])
2381 @expectedFailureIfModuleIsNotAvailable('ifb')
2383 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2386 self
.wait_online(['ifb99:degraded'])
2388 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2396 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2397 def test_l2tp_udp(self
):
2398 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2399 '25-l2tp-udp.netdev', '25-l2tp.network')
2402 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2404 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2406 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2407 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2408 self
.assertRegex(output
, "Peer tunnel 11")
2409 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2410 self
.assertRegex(output
, "UDP checksum: enabled")
2412 output
= check_output('ip l2tp show session tid 10 session_id 15')
2414 self
.assertRegex(output
, "Session 15 in tunnel 10")
2415 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2416 self
.assertRegex(output
, "interface name: l2tp-ses1")
2418 output
= check_output('ip l2tp show session tid 10 session_id 17')
2420 self
.assertRegex(output
, "Session 17 in tunnel 10")
2421 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2422 self
.assertRegex(output
, "interface name: l2tp-ses2")
2424 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2425 def test_l2tp_ip(self
):
2426 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2427 '25-l2tp-ip.netdev', '25-l2tp.network')
2430 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2432 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2434 self
.assertRegex(output
, "Tunnel 10, encap IP")
2435 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2436 self
.assertRegex(output
, "Peer tunnel 12")
2438 output
= check_output('ip l2tp show session tid 10 session_id 25')
2440 self
.assertRegex(output
, "Session 25 in tunnel 10")
2441 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2442 self
.assertRegex(output
, "interface name: l2tp-ses3")
2444 output
= check_output('ip l2tp show session tid 10 session_id 27')
2446 self
.assertRegex(output
, "Session 27 in tunnel 10")
2447 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2448 self
.assertRegex(output
, "interface name: l2tp-ses4")
2450 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2458 def verify_address_static(
2488 output
= check_output('ip address show dev dummy98')
2492 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2493 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2494 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2495 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2496 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2497 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2500 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2501 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2502 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2505 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2506 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2507 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2510 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2511 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2512 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2513 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2514 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2515 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2518 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2519 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2522 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2523 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2524 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2525 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2528 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2529 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2531 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2533 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2535 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2537 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2540 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2541 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2542 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2543 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2546 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2547 prefix16
= ip4_null_16
[:-len('.0.1')]
2548 self
.assertTrue(ip4_null_24
.endswith('.1'))
2549 prefix24
= ip4_null_24
[:-len('.1')]
2550 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2551 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2552 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2553 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2556 self
.assertNotIn('10.4.4.1', output
)
2557 self
.assertNotIn('10.5.4.1', output
)
2558 self
.assertNotIn('10.5.5.1', output
)
2559 self
.assertNotIn('10.8.2.1', output
)
2560 self
.assertNotIn('10.9.3.1', output
)
2561 self
.assertNotIn('2001:db8:0:f101::2', output
)
2562 self
.assertNotIn('2001:db8:0:f103::4', output
)
2565 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2567 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2570 def test_address_static(self
):
2571 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2572 self
.setup_nftset('addr4', 'ipv4_addr')
2573 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2574 self
.setup_nftset('ifindex', 'iface_index')
2577 self
.wait_online(['dummy98:routable'])
2581 output
= check_output('ip -4 --json address show dev dummy98')
2582 for i
in json
.loads(output
)[0]['addr_info']:
2583 if i
['label'] == 'subnet16':
2584 ip4_null_16
= i
['local']
2585 elif i
['label'] == 'subnet24':
2586 ip4_null_24
= i
['local']
2587 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2588 self
.assertTrue(ip4_null_24
.endswith('.1'))
2592 output
= check_output('ip -6 --json address show dev dummy98')
2593 for i
in json
.loads(output
)[0]['addr_info']:
2594 if i
['prefixlen'] == 73:
2595 ip6_null_73
= i
['local']
2596 elif i
['prefixlen'] == 74:
2597 ip6_null_74
= i
['local']
2598 self
.assertTrue(ip6_null_73
.endswith(':1'))
2599 self
.assertTrue(ip6_null_74
.endswith(':1'))
2601 self
.verify_address_static(
2606 broadcast2
=' brd 10.4.2.255',
2607 broadcast3
=' brd 10.4.3.63',
2608 peer1
=' peer 10.5.1.101/24',
2609 peer2
=' peer 10.5.2.101/24',
2610 peer3
='/24 brd 10.5.3.255',
2611 peer4
=' peer 2001:db8:0:f103::101/128',
2612 peer5
=' peer 2001:db8:0:f103::102/128',
2617 deprecated2
=' deprecated',
2619 deprecated4
=' deprecated',
2621 flag1
=' noprefixroute',
2623 flag3
=' noprefixroute',
2624 flag4
=' home mngtmpaddr',
2625 ip4_null_16
=ip4_null_16
,
2626 ip4_null_24
=ip4_null_24
,
2627 ip6_null_73
=ip6_null_73
,
2628 ip6_null_74
=ip6_null_74
,
2631 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2632 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2633 self
.check_nftset('ifindex', 'dummy98')
2635 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2637 copy_network_unit('25-address-static.network.d/10-override.conf')
2639 self
.wait_online(['dummy98:routable'])
2640 self
.verify_address_static(
2641 label1
='new-label1',
2643 label3
='new-label3',
2644 broadcast1
=' brd 10.4.1.255',
2646 broadcast3
=' brd 10.4.3.31',
2647 peer1
=' peer 10.5.1.102/24',
2648 peer2
='/24 brd 10.5.2.255',
2649 peer3
=' peer 10.5.3.102/24',
2650 peer4
=' peer 2001:db8:0:f103::201/128',
2652 peer6
=' peer 2001:db8:0:f103::203/128',
2655 deprecated1
=' deprecated',
2657 deprecated3
=' deprecated',
2661 flag2
=' noprefixroute',
2662 flag3
=' home mngtmpaddr',
2663 flag4
=' noprefixroute',
2664 ip4_null_16
=ip4_null_16
,
2665 ip4_null_24
=ip4_null_24
,
2666 ip6_null_73
=ip6_null_73
,
2667 ip6_null_74
=ip6_null_74
,
2670 networkctl_reconfigure('dummy98')
2671 self
.wait_online(['dummy98:routable'])
2672 self
.verify_address_static(
2673 label1
='new-label1',
2675 label3
='new-label3',
2676 broadcast1
=' brd 10.4.1.255',
2678 broadcast3
=' brd 10.4.3.31',
2679 peer1
=' peer 10.5.1.102/24',
2680 peer2
='/24 brd 10.5.2.255',
2681 peer3
=' peer 10.5.3.102/24',
2682 peer4
=' peer 2001:db8:0:f103::201/128',
2684 peer6
=' peer 2001:db8:0:f103::203/128',
2687 deprecated1
=' deprecated',
2689 deprecated3
=' deprecated',
2693 flag2
=' noprefixroute',
2694 flag3
=' home mngtmpaddr',
2695 flag4
=' noprefixroute',
2696 ip4_null_16
=ip4_null_16
,
2697 ip4_null_24
=ip4_null_24
,
2698 ip6_null_73
=ip6_null_73
,
2699 ip6_null_74
=ip6_null_74
,
2703 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2704 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2705 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2706 output
= check_output('ip address show dev dummy98')
2708 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2709 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2711 # 2. reconfigure the interface, and check the deprecated flag is set again
2712 networkctl_reconfigure('dummy98')
2713 self
.wait_online(['dummy98:routable'])
2714 self
.verify_address_static(
2715 label1
='new-label1',
2717 label3
='new-label3',
2718 broadcast1
=' brd 10.4.1.255',
2720 broadcast3
=' brd 10.4.3.31',
2721 peer1
=' peer 10.5.1.102/24',
2722 peer2
='/24 brd 10.5.2.255',
2723 peer3
=' peer 10.5.3.102/24',
2724 peer4
=' peer 2001:db8:0:f103::201/128',
2726 peer6
=' peer 2001:db8:0:f103::203/128',
2729 deprecated1
=' deprecated',
2731 deprecated3
=' deprecated',
2735 flag2
=' noprefixroute',
2736 flag3
=' home mngtmpaddr',
2737 flag4
=' noprefixroute',
2738 ip4_null_16
=ip4_null_16
,
2739 ip4_null_24
=ip4_null_24
,
2740 ip6_null_73
=ip6_null_73
,
2741 ip6_null_74
=ip6_null_74
,
2744 # test for ENOBUFS issue #17012 (with reload)
2745 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2747 self
.wait_online(['dummy98:routable'])
2748 output
= check_output('ip -4 address show dev dummy98')
2749 for i
in range(1, 254):
2750 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2752 # (with reconfigure)
2753 networkctl_reconfigure('dummy98')
2754 self
.wait_online(['dummy98:routable'])
2755 output
= check_output('ip -4 address show dev dummy98')
2756 for i
in range(1, 254):
2757 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2759 # test for an empty string assignment for Address= in [Network]
2760 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2762 self
.wait_online(['dummy98:routable'])
2763 output
= check_output('ip -4 address show dev dummy98')
2764 for i
in range(1, 254):
2765 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2766 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2768 def test_address_ipv4acd(self
):
2769 check_output('ip netns add ns99')
2770 check_output('ip link add veth99 type veth peer veth-peer')
2771 check_output('ip link set veth-peer netns ns99')
2772 check_output('ip link set veth99 up')
2773 check_output('ip netns exec ns99 ip link set veth-peer up')
2774 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2776 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2778 self
.wait_online(['veth99:routable'])
2780 output
= check_output('ip -4 address show dev veth99')
2782 self
.assertNotIn('192.168.100.10/24', output
)
2783 self
.assertIn('192.168.100.11/24', output
)
2785 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2787 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2789 output
= check_output('ip -4 address show dev veth99')
2791 self
.assertNotIn('192.168.100.10/24', output
)
2792 self
.assertIn('192.168.100.11/24', output
)
2794 def test_address_peer_ipv4(self
):
2795 # test for issue #17304
2796 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2798 for trial
in range(2):
2804 self
.wait_online(['dummy98:routable'])
2806 output
= check_output('ip -4 address show dev dummy98')
2807 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2809 @expectedFailureIfModuleIsNotAvailable('vrf')
2810 def test_prefix_route(self
):
2811 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2812 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2813 '25-vrf.netdev', '25-vrf.network')
2814 for trial
in range(2):
2820 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2822 output
= check_output('ip route show table 42 dev dummy98')
2823 print('### ip route show table 42 dev dummy98')
2825 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2826 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2827 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2828 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2829 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2830 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2831 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2832 output
= check_output('ip -6 route show table 42 dev dummy98')
2833 print('### ip -6 route show table 42 dev dummy98')
2837 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2838 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2839 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2840 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2841 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2842 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2843 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2844 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2848 output
= check_output('ip route show dev test1')
2849 print('### ip route show dev test1')
2851 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2852 output
= check_output('ip route show table local dev test1')
2853 print('### ip route show table local dev test1')
2855 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2856 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2857 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2858 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2859 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2860 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2861 output
= check_output('ip -6 route show dev test1')
2862 print('### ip -6 route show dev test1')
2864 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2865 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2866 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2867 output
= check_output('ip -6 route show table local dev test1')
2868 print('### ip -6 route show table local dev test1')
2870 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2871 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2872 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2873 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2874 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2876 def test_configure_without_carrier(self
):
2877 copy_network_unit('11-dummy.netdev')
2879 self
.wait_operstate('test1', 'off', '')
2880 check_output('ip link set dev test1 up carrier off')
2882 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2884 self
.wait_online(['test1:no-carrier'])
2886 carrier_map
= {'on': '1', 'off': '0'}
2887 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2888 for carrier
in ['off', 'on', 'off']:
2889 with self
.subTest(carrier
=carrier
):
2890 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2891 check_output(f
'ip link set dev test1 carrier {carrier}')
2892 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2894 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2896 self
.assertRegex(output
, '192.168.0.15')
2897 self
.assertRegex(output
, '192.168.0.1')
2898 self
.assertRegex(output
, routable_map
[carrier
])
2900 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2901 copy_network_unit('11-dummy.netdev')
2903 self
.wait_operstate('test1', 'off', '')
2904 check_output('ip link set dev test1 up carrier off')
2906 copy_network_unit('25-test1.network')
2908 self
.wait_online(['test1:no-carrier'])
2910 carrier_map
= {'on': '1', 'off': '0'}
2911 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2912 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2913 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2914 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2915 check_output(f
'ip link set dev test1 carrier {carrier}')
2916 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2918 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2921 self
.assertRegex(output
, '192.168.0.15')
2922 self
.assertRegex(output
, '192.168.0.1')
2924 self
.assertNotRegex(output
, '192.168.0.15')
2925 self
.assertNotRegex(output
, '192.168.0.1')
2926 self
.assertRegex(output
, routable_map
[carrier
])
2928 def test_routing_policy_rule(self
):
2929 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2931 self
.wait_online(['test1:degraded'])
2933 output
= check_output('ip rule list iif test1 priority 111')
2935 self
.assertRegex(output
, '111:')
2936 self
.assertRegex(output
, 'from 192.168.100.18')
2937 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2938 self
.assertRegex(output
, 'iif test1')
2939 self
.assertRegex(output
, 'oif test1')
2940 self
.assertRegex(output
, 'lookup 7')
2942 output
= check_output('ip rule list iif test1 priority 101')
2944 self
.assertRegex(output
, '101:')
2945 self
.assertRegex(output
, 'from all')
2946 self
.assertRegex(output
, 'iif test1')
2947 self
.assertRegex(output
, 'lookup 9')
2949 output
= check_output('ip -6 rule list iif test1 priority 100')
2951 self
.assertRegex(output
, '100:')
2952 self
.assertRegex(output
, 'from all')
2953 self
.assertRegex(output
, 'iif test1')
2954 self
.assertRegex(output
, 'lookup 8')
2956 output
= check_output('ip rule list iif test1 priority 102')
2958 self
.assertRegex(output
, '102:')
2959 self
.assertRegex(output
, 'from 0.0.0.0/8')
2960 self
.assertRegex(output
, 'iif test1')
2961 self
.assertRegex(output
, 'lookup 10')
2963 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2966 def test_routing_policy_rule_issue_11280(self
):
2967 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
2968 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
2970 for trial
in range(3):
2971 restart_networkd(show_logs
=(trial
> 0))
2972 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
2974 output
= check_output('ip rule list table 7')
2976 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2978 output
= check_output('ip rule list table 8')
2980 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2982 def test_routing_policy_rule_reconfigure(self
):
2983 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
2985 self
.wait_online(['test1:degraded'])
2987 output
= check_output('ip rule list table 1011')
2989 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2990 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2991 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2992 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2994 output
= check_output('ip -6 rule list table 1011')
2996 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2998 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3000 self
.wait_online(['test1:degraded'])
3002 output
= check_output('ip rule list table 1011')
3004 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3005 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3006 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3007 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3009 output
= check_output('ip -6 rule list table 1011')
3011 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3012 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3014 call('ip rule delete priority 10111')
3015 call('ip rule delete priority 10112')
3016 call('ip rule delete priority 10113')
3017 call('ip rule delete priority 10114')
3018 call('ip -6 rule delete priority 10113')
3020 output
= check_output('ip rule list table 1011')
3022 self
.assertEqual(output
, '')
3024 output
= check_output('ip -6 rule list table 1011')
3026 self
.assertEqual(output
, '')
3028 networkctl_reconfigure('test1')
3029 self
.wait_online(['test1:degraded'])
3031 output
= check_output('ip rule list table 1011')
3033 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3034 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3035 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3036 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3038 output
= check_output('ip -6 rule list table 1011')
3040 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3042 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3043 def test_routing_policy_rule_port_range(self
):
3044 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3046 self
.wait_online(['test1:degraded'])
3048 output
= check_output('ip rule')
3050 self
.assertRegex(output
, '111')
3051 self
.assertRegex(output
, 'from 192.168.100.18')
3052 self
.assertRegex(output
, '1123-1150')
3053 self
.assertRegex(output
, '3224-3290')
3054 self
.assertRegex(output
, 'tcp')
3055 self
.assertRegex(output
, 'lookup 7')
3057 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3058 def test_routing_policy_rule_invert(self
):
3059 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3061 self
.wait_online(['test1:degraded'])
3063 output
= check_output('ip rule')
3065 self
.assertRegex(output
, '111')
3066 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3067 self
.assertRegex(output
, 'tcp')
3068 self
.assertRegex(output
, 'lookup 7')
3070 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3071 def test_routing_policy_rule_uidrange(self
):
3072 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3074 self
.wait_online(['test1:degraded'])
3076 output
= check_output('ip rule')
3078 self
.assertRegex(output
, '111')
3079 self
.assertRegex(output
, 'from 192.168.100.18')
3080 self
.assertRegex(output
, 'lookup 7')
3081 self
.assertRegex(output
, 'uidrange 100-200')
3083 def _test_route_static(self
, manage_foreign_routes
):
3084 if not manage_foreign_routes
:
3085 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3087 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3088 '25-route-static-test1.network', '11-dummy.netdev')
3090 self
.wait_online(['dummy98:routable'])
3092 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3095 print('### ip -6 route show dev dummy98')
3096 output
= check_output('ip -6 route show dev dummy98')
3098 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3099 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3100 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3102 print('### ip -6 route show default')
3103 output
= check_output('ip -6 route show default')
3105 self
.assertIn('default', output
)
3106 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3108 print('### ip -4 route show dev dummy98')
3109 output
= check_output('ip -4 route show dev dummy98')
3111 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3112 self
.assertIn('149.10.124.64 proto static scope link', output
)
3113 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3114 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3115 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3116 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3117 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3118 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3120 print('### ip -4 route show dev dummy98 default')
3121 output
= check_output('ip -4 route show dev dummy98 default')
3123 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3124 self
.assertIn('default via 149.10.124.64 proto static', output
)
3125 self
.assertIn('default proto static', output
)
3126 self
.assertIn('default via 1.1.8.104 proto static', output
)
3128 print('### ip -4 route show table local dev dummy98')
3129 output
= check_output('ip -4 route show table local dev dummy98')
3131 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3132 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3133 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3135 print('### ip -4 route show type blackhole')
3136 output
= check_output('ip -4 route show type blackhole')
3138 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3140 print('### ip -4 route show type unreachable')
3141 output
= check_output('ip -4 route show type unreachable')
3143 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3145 print('### ip -4 route show type prohibit')
3146 output
= check_output('ip -4 route show type prohibit')
3148 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3150 print('### ip -6 route show type blackhole')
3151 output
= check_output('ip -6 route show type blackhole')
3153 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3155 print('### ip -6 route show type unreachable')
3156 output
= check_output('ip -6 route show type unreachable')
3158 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3160 print('### ip -6 route show type prohibit')
3161 output
= check_output('ip -6 route show type prohibit')
3163 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3165 print('### ip route show 192.168.10.1')
3166 output
= check_output('ip route show 192.168.10.1')
3168 self
.assertIn('192.168.10.1 proto static', output
)
3169 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3170 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3171 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3172 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3174 print('### ip route show 192.168.10.2')
3175 output
= check_output('ip route show 192.168.10.2')
3177 # old ip command does not show IPv6 gateways...
3178 self
.assertIn('192.168.10.2 proto static', output
)
3179 self
.assertIn('nexthop', output
)
3180 self
.assertIn('dev test1 weight 20', output
)
3181 self
.assertIn('dev test1 weight 30', output
)
3182 self
.assertIn('dev dummy98 weight 10', output
)
3183 self
.assertIn('dev dummy98 weight 5', output
)
3185 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3186 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3188 # old ip command does not show 'nexthop' keyword and weight...
3189 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3190 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3191 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3192 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3193 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3195 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3198 copy_network_unit('25-address-static.network', copy_dropins
=False)
3200 self
.wait_online(['dummy98:routable'])
3202 # check all routes managed by Manager are removed
3203 print('### ip -4 route show type blackhole')
3204 output
= check_output('ip -4 route show type blackhole')
3206 self
.assertEqual(output
, '')
3208 print('### ip -4 route show type unreachable')
3209 output
= check_output('ip -4 route show type unreachable')
3211 self
.assertEqual(output
, '')
3213 print('### ip -4 route show type prohibit')
3214 output
= check_output('ip -4 route show type prohibit')
3216 self
.assertEqual(output
, '')
3218 print('### ip -6 route show type blackhole')
3219 output
= check_output('ip -6 route show type blackhole')
3221 self
.assertEqual(output
, '')
3223 print('### ip -6 route show type unreachable')
3224 output
= check_output('ip -6 route show type unreachable')
3226 self
.assertEqual(output
, '')
3228 print('### ip -6 route show type prohibit')
3229 output
= check_output('ip -6 route show type prohibit')
3231 self
.assertEqual(output
, '')
3233 remove_network_unit('25-address-static.network')
3235 self
.wait_online(['dummy98:routable'])
3237 # check all routes managed by Manager are reconfigured
3238 print('### ip -4 route show type blackhole')
3239 output
= check_output('ip -4 route show type blackhole')
3241 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3243 print('### ip -4 route show type unreachable')
3244 output
= check_output('ip -4 route show type unreachable')
3246 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3248 print('### ip -4 route show type prohibit')
3249 output
= check_output('ip -4 route show type prohibit')
3251 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3253 print('### ip -6 route show type blackhole')
3254 output
= check_output('ip -6 route show type blackhole')
3256 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3258 print('### ip -6 route show type unreachable')
3259 output
= check_output('ip -6 route show type unreachable')
3261 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3263 print('### ip -6 route show type prohibit')
3264 output
= check_output('ip -6 route show type prohibit')
3266 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3268 remove_link('dummy98')
3271 # check all routes managed by Manager are removed
3272 print('### ip -4 route show type blackhole')
3273 output
= check_output('ip -4 route show type blackhole')
3275 self
.assertEqual(output
, '')
3277 print('### ip -4 route show type unreachable')
3278 output
= check_output('ip -4 route show type unreachable')
3280 self
.assertEqual(output
, '')
3282 print('### ip -4 route show type prohibit')
3283 output
= check_output('ip -4 route show type prohibit')
3285 self
.assertEqual(output
, '')
3287 print('### ip -6 route show type blackhole')
3288 output
= check_output('ip -6 route show type blackhole')
3290 self
.assertEqual(output
, '')
3292 print('### ip -6 route show type unreachable')
3293 output
= check_output('ip -6 route show type unreachable')
3295 self
.assertEqual(output
, '')
3297 print('### ip -6 route show type prohibit')
3298 output
= check_output('ip -6 route show type prohibit')
3300 self
.assertEqual(output
, '')
3302 def test_route_static(self
):
3304 for manage_foreign_routes
in [True, False]:
3310 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3311 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3312 self
._test
_route
_static
(manage_foreign_routes
)
3314 @expectedFailureIfRTA_VIAIsNotSupported()
3315 def test_route_via_ipv6(self
):
3316 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3318 self
.wait_online(['dummy98:routable'])
3320 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3323 print('### ip -6 route show dev dummy98')
3324 output
= check_output('ip -6 route show dev dummy98')
3326 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3327 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3329 print('### ip -4 route show dev dummy98')
3330 output
= check_output('ip -4 route show dev dummy98')
3332 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3333 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3335 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3336 def test_route_congctl(self
):
3337 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3339 self
.wait_online(['dummy98:routable'])
3341 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3342 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3344 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3345 self
.assertIn('congctl dctcp', output
)
3347 print('### ip -4 route show dev dummy98 149.10.124.66')
3348 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3350 self
.assertIn('149.10.124.66 proto static', output
)
3351 self
.assertIn('congctl dctcp', output
)
3352 self
.assertIn('rto_min 300s', output
)
3354 @expectedFailureIfModuleIsNotAvailable('vrf')
3355 def test_route_vrf(self
):
3356 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3357 '25-vrf.netdev', '25-vrf.network')
3359 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3361 output
= check_output('ip route show vrf vrf99')
3363 self
.assertRegex(output
, 'default via 192.168.100.1')
3365 output
= check_output('ip route show')
3367 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3369 def test_gateway_reconfigure(self
):
3370 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3372 self
.wait_online(['dummy98:routable'])
3373 print('### ip -4 route show dev dummy98 default')
3374 output
= check_output('ip -4 route show dev dummy98 default')
3376 self
.assertIn('default via 149.10.124.59 proto static', output
)
3377 self
.assertNotIn('149.10.124.60', output
)
3379 remove_network_unit('25-gateway-static.network')
3380 copy_network_unit('25-gateway-next-static.network')
3382 self
.wait_online(['dummy98:routable'])
3383 print('### ip -4 route show dev dummy98 default')
3384 output
= check_output('ip -4 route show dev dummy98 default')
3386 self
.assertNotIn('149.10.124.59', output
)
3387 self
.assertIn('default via 149.10.124.60 proto static', output
)
3389 def test_ip_route_ipv6_src_route(self
):
3390 # a dummy device does not make the addresses go through tentative state, so we
3391 # reuse a bond from an earlier test, which does make the addresses go through
3392 # tentative state, and do our test on that
3393 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3395 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3397 output
= check_output('ip -6 route list dev bond199')
3399 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3401 def test_route_preferred_source_with_existing_address(self
):
3403 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3408 networkctl_reconfigure('dummy98')
3410 self
.wait_online(['dummy98:routable'])
3412 output
= check_output('ip -6 route list dev dummy98')
3414 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3416 def test_ip_link_mac_address(self
):
3417 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3419 self
.wait_online(['dummy98:degraded'])
3421 output
= check_output('ip link show dummy98')
3423 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3425 def test_ip_link_unmanaged(self
):
3426 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3429 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3431 def test_ipv6_address_label(self
):
3432 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3434 self
.wait_online(['dummy98:degraded'])
3436 output
= check_output('ip addrlabel list')
3438 self
.assertRegex(output
, '2004:da8:1::/64')
3440 def test_ipv6_proxy_ndp(self
):
3441 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3444 self
.wait_online(['dummy98:routable'])
3446 output
= check_output('ip neighbor show proxy dev dummy98')
3448 for i
in range(1, 5):
3449 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3451 def test_neighbor_section(self
):
3452 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3454 self
.wait_online(['dummy98:degraded'])
3456 print('### ip neigh list dev dummy98')
3457 output
= check_output('ip neigh list dev dummy98')
3459 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3460 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3461 self
.assertNotIn('2004:da8:1:0::2', output
)
3462 self
.assertNotIn('192.168.10.2', output
)
3463 self
.assertNotIn('00:00:5e:00:02:67', output
)
3465 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3468 copy_network_unit('25-neighbor-section.network.d/override.conf')
3470 self
.wait_online(['dummy98:degraded'])
3472 print('### ip neigh list dev dummy98 (after reloading)')
3473 output
= check_output('ip neigh list dev dummy98')
3475 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3476 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3477 self
.assertNotIn('2004:da8:1:0::2', output
)
3478 self
.assertNotIn('192.168.10.2', output
)
3479 self
.assertNotIn('00:00:5e:00:02', output
)
3481 def test_neighbor_reconfigure(self
):
3482 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3484 self
.wait_online(['dummy98:degraded'])
3486 print('### ip neigh list dev dummy98')
3487 output
= check_output('ip neigh list dev dummy98')
3489 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3490 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3492 remove_network_unit('25-neighbor-section.network')
3493 copy_network_unit('25-neighbor-next.network')
3495 self
.wait_online(['dummy98:degraded'])
3496 print('### ip neigh list dev dummy98')
3497 output
= check_output('ip neigh list dev dummy98')
3499 self
.assertNotIn('00:00:5e:00:02:65', output
)
3500 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3501 self
.assertNotIn('2004:da8:1::1', output
)
3503 def test_neighbor_gre(self
):
3504 copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
3505 '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
3507 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout
='40s')
3509 output
= check_output('ip neigh list dev gretun97')
3511 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3512 self
.assertNotIn('10.0.0.23', output
)
3514 output
= check_output('ip neigh list dev ip6gretun97')
3516 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3517 self
.assertNotIn('2001:db8:0:f102::18', output
)
3519 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3522 def test_link_local_addressing(self
):
3523 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3524 '25-link-local-addressing-no.network', '12-dummy.netdev')
3526 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3528 output
= check_output('ip address show dev test1')
3530 self
.assertRegex(output
, 'inet .* scope link')
3531 self
.assertRegex(output
, 'inet6 .* scope link')
3533 output
= check_output('ip address show dev dummy98')
3535 self
.assertNotRegex(output
, 'inet6* .* scope link')
3537 # Documentation/networking/ip-sysctl.txt
3539 # addr_gen_mode - INTEGER
3540 # Defines how link-local and autoconf addresses are generated.
3542 # 0: generate address based on EUI64 (default)
3543 # 1: do no generate a link-local address, use EUI64 for addresses generated
3545 # 2: generate stable privacy addresses, using the secret from
3546 # stable_secret (RFC7217)
3547 # 3: generate stable privacy addresses, using a random secret if unset
3549 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3550 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3551 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3553 def test_link_local_addressing_ipv6ll(self
):
3554 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3556 self
.wait_online(['dummy98:degraded'])
3558 # An IPv6LL address exists by default.
3559 output
= check_output('ip address show dev dummy98')
3561 self
.assertRegex(output
, 'inet6 .* scope link')
3563 copy_network_unit('25-link-local-addressing-no.network')
3565 self
.wait_online(['dummy98:carrier'])
3567 # Check if the IPv6LL address is removed.
3568 output
= check_output('ip address show dev dummy98')
3570 self
.assertNotRegex(output
, 'inet6 .* scope link')
3572 remove_network_unit('25-link-local-addressing-no.network')
3574 self
.wait_online(['dummy98:degraded'])
3576 # Check if a new IPv6LL address is assigned.
3577 output
= check_output('ip address show dev dummy98')
3579 self
.assertRegex(output
, 'inet6 .* scope link')
3581 @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
3582 def test_sysctl(self
):
3583 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3584 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3586 self
.wait_online(['dummy98:degraded'])
3588 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3589 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3590 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3591 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3592 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3593 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3594 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3595 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3596 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3598 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3600 self
.wait_online(['dummy98:degraded'])
3602 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3604 def test_sysctl_disable_ipv6(self
):
3605 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3607 print('## Disable ipv6')
3608 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3609 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3612 self
.wait_online(['dummy98:routable'])
3614 output
= check_output('ip -4 address show dummy98')
3616 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3617 output
= check_output('ip -6 address show dummy98')
3619 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3620 self
.assertRegex(output
, 'inet6 .* scope link')
3621 output
= check_output('ip -4 route show dev dummy98')
3623 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3624 output
= check_output('ip -6 route show default')
3626 self
.assertRegex(output
, 'default')
3627 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3629 remove_link('dummy98')
3631 print('## Enable ipv6')
3632 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3633 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3636 self
.wait_online(['dummy98:routable'])
3638 output
= check_output('ip -4 address show dummy98')
3640 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3641 output
= check_output('ip -6 address show dummy98')
3643 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3644 self
.assertRegex(output
, 'inet6 .* scope link')
3645 output
= check_output('ip -4 route show dev dummy98')
3647 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3648 output
= check_output('ip -6 route show default')
3650 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3652 def test_bind_carrier(self
):
3653 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3656 # no bound interface.
3657 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3658 output
= check_output('ip address show test1')
3660 self
.assertNotIn('UP,LOWER_UP', output
)
3661 self
.assertIn('DOWN', output
)
3662 self
.assertNotIn('192.168.10', output
)
3664 # add one bound interface. The interface will be up.
3665 check_output('ip link add dummy98 type dummy')
3666 check_output('ip link set dummy98 up')
3667 self
.wait_online(['test1:routable'])
3668 output
= check_output('ip address show test1')
3670 self
.assertIn('UP,LOWER_UP', output
)
3671 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3673 # add another bound interface. The interface is still up.
3674 check_output('ip link add dummy99 type dummy')
3675 check_output('ip link set dummy99 up')
3676 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3677 output
= check_output('ip address show test1')
3679 self
.assertIn('UP,LOWER_UP', output
)
3680 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3682 # remove one of the bound interfaces. The interface is still up
3683 remove_link('dummy98')
3684 output
= check_output('ip address show test1')
3686 self
.assertIn('UP,LOWER_UP', output
)
3687 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3689 # bring down the remaining bound interface. The interface will be down.
3690 check_output('ip link set dummy99 down')
3691 self
.wait_operstate('test1', 'off')
3692 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3693 output
= check_output('ip address show test1')
3695 self
.assertNotIn('UP,LOWER_UP', output
)
3696 self
.assertIn('DOWN', output
)
3697 self
.assertNotIn('192.168.10', output
)
3699 # bring up the bound interface. The interface will be up.
3700 check_output('ip link set dummy99 up')
3701 self
.wait_online(['test1:routable'])
3702 output
= check_output('ip address show test1')
3704 self
.assertIn('UP,LOWER_UP', output
)
3705 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3707 # remove the remaining bound interface. The interface will be down.
3708 remove_link('dummy99')
3709 self
.wait_operstate('test1', 'off')
3710 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3711 output
= check_output('ip address show test1')
3713 self
.assertNotIn('UP,LOWER_UP', output
)
3714 self
.assertIn('DOWN', output
)
3715 self
.assertNotIn('192.168.10', output
)
3717 # re-add one bound interface. The interface will be up.
3718 check_output('ip link add dummy98 type dummy')
3719 check_output('ip link set dummy98 up')
3720 self
.wait_online(['test1:routable'])
3721 output
= check_output('ip address show test1')
3723 self
.assertIn('UP,LOWER_UP', output
)
3724 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3726 def _test_activation_policy(self
, interface
, test
):
3727 conffile
= '25-activation-policy.network'
3729 conffile
= f
'{conffile}.d/{test}.conf'
3730 if interface
== 'vlan99':
3731 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3732 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3735 always
= test
.startswith('always')
3736 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3737 expect_up
= initial_up
3738 next_up
= not expect_up
3740 if test
.endswith('down'):
3741 self
.wait_activated(interface
)
3743 for iteration
in range(4):
3744 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3745 operstate
= 'routable' if expect_up
else 'off'
3746 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3747 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3750 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3751 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3752 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3754 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3757 check_output(f
'ip link set dev {interface} up')
3759 check_output(f
'ip link set dev {interface} down')
3760 expect_up
= initial_up
if always
else next_up
3761 next_up
= not next_up
3765 def test_activation_policy(self
):
3767 for interface
in ['test1', 'vlan99']:
3768 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3774 print(f
'### test_activation_policy(interface={interface}, test={test})')
3775 with self
.subTest(interface
=interface
, test
=test
):
3776 self
._test
_activation
_policy
(interface
, test
)
3778 def _test_activation_policy_required_for_online(self
, policy
, required
):
3779 conffile
= '25-activation-policy.network'
3780 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3782 units
+= [f
'{conffile}.d/{policy}.conf']
3784 units
+= [f
'{conffile}.d/required-{required}.conf']
3785 copy_network_unit(*units
, copy_dropins
=False)
3788 if policy
.endswith('down'):
3789 self
.wait_activated('test1')
3791 if policy
.endswith('down') or policy
== 'manual':
3792 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3794 self
.wait_online(['test1'])
3796 if policy
== 'always-down':
3797 # if always-down, required for online is forced to no
3800 # otherwise if required for online is specified, it should match that
3801 expected
= required
== 'yes'
3803 # otherwise if only policy specified, required for online defaults to
3804 # true if policy is up, always-up, or bound
3805 expected
= policy
.endswith('up') or policy
== 'bound'
3807 # default is true, if neither are specified
3810 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
3813 yesno
= 'yes' if expected
else 'no'
3814 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3816 def test_activation_policy_required_for_online(self
):
3818 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3819 for required
in ['yes', 'no', '']:
3825 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3826 with self
.subTest(policy
=policy
, required
=required
):
3827 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3829 def test_domain(self
):
3830 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3832 self
.wait_online(['dummy98:routable'])
3834 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3836 self
.assertRegex(output
, 'Address: 192.168.42.100')
3837 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3838 self
.assertRegex(output
, 'Search Domains: one')
3840 def test_keep_configuration_static(self
):
3841 check_output('ip link add name dummy98 type dummy')
3842 check_output('ip address add 10.1.2.3/16 dev dummy98')
3843 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3844 output
= check_output('ip address show dummy98')
3846 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3847 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3848 output
= check_output('ip route show dev dummy98')
3851 copy_network_unit('24-keep-configuration-static.network')
3853 self
.wait_online(['dummy98:routable'])
3855 output
= check_output('ip address show dummy98')
3857 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3858 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3860 def check_nexthop(self
, manage_foreign_nexthops
):
3861 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3863 output
= check_output('ip nexthop list dev veth99')
3865 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3866 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3867 self
.assertIn('id 3 dev veth99', output
)
3868 self
.assertIn('id 4 dev veth99', output
)
3869 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3870 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3871 if manage_foreign_nexthops
:
3872 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3874 output
= check_output('ip nexthop list dev dummy98')
3876 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
3877 if manage_foreign_nexthops
:
3878 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
3880 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
3882 # kernel manages blackhole nexthops on lo
3883 output
= check_output('ip nexthop list dev lo')
3885 self
.assertIn('id 6 blackhole', output
)
3886 self
.assertIn('id 7 blackhole', output
)
3888 # group nexthops are shown with -0 option
3889 output
= check_output('ip -0 nexthop list id 21')
3891 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
3893 output
= check_output('ip route show dev veth99 10.10.10.10')
3895 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
3897 output
= check_output('ip route show dev veth99 10.10.10.11')
3899 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
3901 output
= check_output('ip route show dev veth99 10.10.10.12')
3903 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
3905 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3907 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3909 output
= check_output('ip route show 10.10.10.13')
3911 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
3913 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
3915 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
3917 output
= check_output('ip route show 10.10.10.14')
3919 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
3920 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
3921 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
3923 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3926 def _test_nexthop(self
, manage_foreign_nexthops
):
3927 if not manage_foreign_nexthops
:
3928 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
3930 check_output('ip link add dummy98 type dummy')
3931 check_output('ip link set dummy98 up')
3932 check_output('ip address add 192.168.20.20/24 dev dummy98')
3933 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
3935 copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
3936 '12-dummy.netdev', '25-nexthop-dummy.network')
3939 self
.check_nexthop(manage_foreign_nexthops
)
3941 remove_network_unit('25-nexthop.network')
3942 copy_network_unit('25-nexthop-nothing.network')
3944 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3946 output
= check_output('ip nexthop list dev veth99')
3948 self
.assertEqual(output
, '')
3949 output
= check_output('ip nexthop list dev lo')
3951 self
.assertEqual(output
, '')
3953 remove_network_unit('25-nexthop-nothing.network')
3954 copy_network_unit('25-nexthop.network')
3955 networkctl_reconfigure('dummy98')
3958 self
.check_nexthop(manage_foreign_nexthops
)
3960 remove_link('veth99')
3963 output
= check_output('ip nexthop list dev lo')
3965 self
.assertEqual(output
, '')
3967 @expectedFailureIfNexthopIsNotAvailable()
3968 def test_nexthop(self
):
3970 for manage_foreign_nexthops
in [True, False]:
3976 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
3977 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
3978 self
._test
_nexthop
(manage_foreign_nexthops
)
3980 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
3988 @expectedFailureIfModuleIsNotAvailable('sch_cake')
3989 def test_qdisc_cake(self
):
3990 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
3992 self
.wait_online(['dummy98:routable'])
3994 output
= check_output('tc qdisc show dev dummy98')
3996 self
.assertIn('qdisc cake 3a: root', output
)
3997 self
.assertIn('bandwidth 500Mbit', output
)
3998 self
.assertIn('autorate-ingress', output
)
3999 self
.assertIn('diffserv8', output
)
4000 self
.assertIn('dual-dsthost', output
)
4001 self
.assertIn(' nat', output
)
4002 self
.assertIn(' wash', output
)
4003 self
.assertIn(' split-gso', output
)
4004 self
.assertIn(' raw', output
)
4005 self
.assertIn(' atm', output
)
4006 self
.assertIn('overhead 128', output
)
4007 self
.assertIn('mpu 20', output
)
4008 self
.assertIn('fwmark 0xff00', output
)
4009 self
.assertIn('rtt 1s', output
)
4010 self
.assertIn('ack-filter-aggressive', output
)
4012 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4013 def test_qdisc_codel(self
):
4014 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4016 self
.wait_online(['dummy98:routable'])
4018 output
= check_output('tc qdisc show dev dummy98')
4020 self
.assertRegex(output
, 'qdisc codel 33: root')
4021 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4023 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4024 def test_qdisc_drr(self
):
4025 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4027 self
.wait_online(['dummy98:routable'])
4029 output
= check_output('tc qdisc show dev dummy98')
4031 self
.assertRegex(output
, 'qdisc drr 2: root')
4032 output
= check_output('tc class show dev dummy98')
4034 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4036 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4037 def test_qdisc_ets(self
):
4038 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4040 self
.wait_online(['dummy98:routable'])
4042 output
= check_output('tc qdisc show dev dummy98')
4045 self
.assertRegex(output
, 'qdisc ets 3a: root')
4046 self
.assertRegex(output
, 'bands 10 strict 3')
4047 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4048 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4050 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4051 def test_qdisc_fq(self
):
4052 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4054 self
.wait_online(['dummy98:routable'])
4056 output
= check_output('tc qdisc show dev dummy98')
4058 self
.assertRegex(output
, 'qdisc fq 32: root')
4059 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4060 self
.assertRegex(output
, 'quantum 1500')
4061 self
.assertRegex(output
, 'initial_quantum 13000')
4062 self
.assertRegex(output
, 'maxrate 1Mbit')
4064 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4065 def test_qdisc_fq_codel(self
):
4066 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4068 self
.wait_online(['dummy98:routable'])
4070 output
= check_output('tc qdisc show dev dummy98')
4072 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4073 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')
4075 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4076 def test_qdisc_fq_pie(self
):
4077 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4079 self
.wait_online(['dummy98:routable'])
4081 output
= check_output('tc qdisc show dev dummy98')
4084 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4085 self
.assertRegex(output
, 'limit 200000p')
4087 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4088 def test_qdisc_gred(self
):
4089 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4091 self
.wait_online(['dummy98:routable'])
4093 output
= check_output('tc qdisc show dev dummy98')
4095 self
.assertRegex(output
, 'qdisc gred 38: root')
4096 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4098 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4099 def test_qdisc_hhf(self
):
4100 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4102 self
.wait_online(['dummy98:routable'])
4104 output
= check_output('tc qdisc show dev dummy98')
4106 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4107 self
.assertRegex(output
, 'limit 1022p')
4109 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4110 def test_qdisc_htb_fifo(self
):
4111 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4113 self
.wait_online(['dummy98:routable'])
4115 output
= check_output('tc qdisc show dev dummy98')
4117 self
.assertRegex(output
, 'qdisc htb 2: root')
4118 self
.assertRegex(output
, r
'default (0x30|30)')
4120 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4121 self
.assertRegex(output
, 'limit 100000p')
4123 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4124 self
.assertRegex(output
, 'limit 1000000')
4126 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4127 self
.assertRegex(output
, 'limit 1023p')
4129 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4131 output
= check_output('tc -d class show dev dummy98')
4133 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4134 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4135 # which is fixed in v6.3.0 by
4136 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4137 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4138 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4139 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4140 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4141 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4142 self
.assertRegex(output
, 'burst 123456')
4143 self
.assertRegex(output
, 'cburst 123457')
4145 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4146 def test_qdisc_ingress(self
):
4147 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4148 '25-qdisc-ingress.network', '11-dummy.netdev')
4150 self
.wait_online(['dummy98:routable', 'test1:routable'])
4152 output
= check_output('tc qdisc show dev dummy98')
4154 self
.assertRegex(output
, 'qdisc clsact')
4156 output
= check_output('tc qdisc show dev test1')
4158 self
.assertRegex(output
, 'qdisc ingress')
4160 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4161 def test_qdisc_netem(self
):
4162 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4163 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4165 self
.wait_online(['dummy98:routable', 'test1:routable'])
4167 output
= check_output('tc qdisc show dev dummy98')
4169 self
.assertRegex(output
, 'qdisc netem 30: root')
4170 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4172 output
= check_output('tc qdisc show dev test1')
4174 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4175 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4177 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4178 def test_qdisc_pie(self
):
4179 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4181 self
.wait_online(['dummy98:routable'])
4183 output
= check_output('tc qdisc show dev dummy98')
4185 self
.assertRegex(output
, 'qdisc pie 3a: root')
4186 self
.assertRegex(output
, 'limit 200000')
4188 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4189 def test_qdisc_qfq(self
):
4190 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4192 self
.wait_online(['dummy98:routable'])
4194 output
= check_output('tc qdisc show dev dummy98')
4196 self
.assertRegex(output
, 'qdisc qfq 2: root')
4197 output
= check_output('tc class show dev dummy98')
4199 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4200 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4202 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4203 def test_qdisc_sfb(self
):
4204 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4206 self
.wait_online(['dummy98:routable'])
4208 output
= check_output('tc qdisc show dev dummy98')
4210 self
.assertRegex(output
, 'qdisc sfb 39: root')
4211 self
.assertRegex(output
, 'limit 200000')
4213 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4214 def test_qdisc_sfq(self
):
4215 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4217 self
.wait_online(['dummy98:routable'])
4219 output
= check_output('tc qdisc show dev dummy98')
4221 self
.assertRegex(output
, 'qdisc sfq 36: root')
4222 self
.assertRegex(output
, 'perturb 5sec')
4224 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4225 def test_qdisc_tbf(self
):
4226 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4228 self
.wait_online(['dummy98:routable'])
4230 output
= check_output('tc qdisc show dev dummy98')
4232 self
.assertRegex(output
, 'qdisc tbf 35: root')
4233 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4235 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4236 def test_qdisc_teql(self
):
4237 call_quiet('rmmod sch_teql')
4239 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4241 self
.wait_links('dummy98')
4242 check_output('modprobe sch_teql max_equalizers=2')
4243 self
.wait_online(['dummy98:routable'])
4245 output
= check_output('tc qdisc show dev dummy98')
4247 self
.assertRegex(output
, 'qdisc teql1 31: root')
4249 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4257 def test_state_file(self
):
4258 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4260 self
.wait_online(['dummy98:routable'])
4262 # make link state file updated
4263 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4265 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4268 output
= read_link_state_file('dummy98')
4270 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4271 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4272 self
.assertIn('ADMIN_STATE=configured', output
)
4273 self
.assertIn('OPER_STATE=routable', output
)
4274 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4275 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4276 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4277 self
.assertIn('ACTIVATION_POLICY=up', output
)
4278 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4279 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4280 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4281 self
.assertIn('DOMAINS=hogehoge', output
)
4282 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4283 self
.assertIn('LLMNR=no', output
)
4284 self
.assertIn('MDNS=yes', output
)
4285 self
.assertIn('DNSSEC=no', output
)
4287 check_output(*resolvectl_cmd
, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env
=env
)
4288 check_output(*resolvectl_cmd
, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env
=env
)
4289 check_output(*resolvectl_cmd
, 'llmnr', 'dummy98', 'yes', env
=env
)
4290 check_output(*resolvectl_cmd
, 'mdns', 'dummy98', 'no', env
=env
)
4291 check_output(*resolvectl_cmd
, 'dnssec', 'dummy98', 'yes', env
=env
)
4292 check_output(*timedatectl_cmd
, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env
=env
)
4294 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4297 output
= read_link_state_file('dummy98')
4299 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4300 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4301 self
.assertIn('DOMAINS=hogehogehoge', output
)
4302 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4303 self
.assertIn('LLMNR=yes', output
)
4304 self
.assertIn('MDNS=no', output
)
4305 self
.assertIn('DNSSEC=yes', output
)
4307 check_output(*timedatectl_cmd
, 'revert', 'dummy98', env
=env
)
4309 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4312 output
= read_link_state_file('dummy98')
4314 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4315 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4316 self
.assertIn('DOMAINS=hogehogehoge', output
)
4317 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4318 self
.assertIn('LLMNR=yes', output
)
4319 self
.assertIn('MDNS=no', output
)
4320 self
.assertIn('DNSSEC=yes', output
)
4322 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4324 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4327 output
= read_link_state_file('dummy98')
4329 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4330 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4331 self
.assertIn('DOMAINS=hogehoge', output
)
4332 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4333 self
.assertIn('LLMNR=no', output
)
4334 self
.assertIn('MDNS=yes', output
)
4335 self
.assertIn('DNSSEC=no', output
)
4337 def test_address_state(self
):
4338 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4341 self
.wait_online(['dummy98:degraded'])
4343 output
= read_link_state_file('dummy98')
4344 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4345 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4347 # with a routable IPv4 address
4348 check_output('ip address add 10.1.2.3/16 dev dummy98')
4349 self
.wait_online(['dummy98:routable'], ipv4
=True)
4350 self
.wait_online(['dummy98:routable'])
4352 output
= read_link_state_file('dummy98')
4353 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4354 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4356 check_output('ip address del 10.1.2.3/16 dev dummy98')
4358 # with a routable IPv6 address
4359 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4360 self
.wait_online(['dummy98:routable'], ipv6
=True)
4361 self
.wait_online(['dummy98:routable'])
4363 output
= read_link_state_file('dummy98')
4364 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4365 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4367 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4375 def test_bond_keep_master(self
):
4376 check_output('ip link add bond199 type bond mode active-backup')
4377 check_output('ip link add dummy98 type dummy')
4378 check_output('ip link set dummy98 master bond199')
4380 copy_network_unit('23-keep-master.network')
4382 self
.wait_online(['dummy98:enslaved'])
4384 output
= check_output('ip -d link show bond199')
4386 self
.assertRegex(output
, 'active_slave dummy98')
4388 output
= check_output('ip -d link show dummy98')
4390 self
.assertRegex(output
, 'master bond199')
4392 def test_bond_active_slave(self
):
4393 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4395 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4397 output
= check_output('ip -d link show bond199')
4399 self
.assertIn('active_slave dummy98', output
)
4401 def test_bond_primary_slave(self
):
4402 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4404 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4406 output
= check_output('ip -d link show bond199')
4408 self
.assertIn('primary dummy98', output
)
4411 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4412 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4413 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4414 f
.write(f
'[Link]\nMACAddress={mac}\n')
4417 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4419 output
= check_output('ip -d link show bond199')
4421 self
.assertIn(f
'link/ether {mac}', output
)
4423 def test_bond_operstate(self
):
4424 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4425 '25-bond99.network', '25-bond-slave.network')
4427 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4429 output
= check_output('ip -d link show dummy98')
4431 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4433 output
= check_output('ip -d link show test1')
4435 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4437 output
= check_output('ip -d link show bond99')
4439 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4441 self
.wait_operstate('dummy98', 'enslaved')
4442 self
.wait_operstate('test1', 'enslaved')
4443 self
.wait_operstate('bond99', 'routable')
4445 check_output('ip link set dummy98 down')
4447 self
.wait_operstate('dummy98', 'off')
4448 self
.wait_operstate('test1', 'enslaved')
4449 self
.wait_operstate('bond99', 'routable')
4451 check_output('ip link set dummy98 up')
4453 self
.wait_operstate('dummy98', 'enslaved')
4454 self
.wait_operstate('test1', 'enslaved')
4455 self
.wait_operstate('bond99', 'routable')
4457 check_output('ip link set dummy98 down')
4458 check_output('ip link set test1 down')
4460 self
.wait_operstate('dummy98', 'off')
4461 self
.wait_operstate('test1', 'off')
4463 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4464 # Huh? Kernel does not recognize that all slave interfaces are down?
4465 # Let's confirm that networkd's operstate is consistent with ip's result.
4466 self
.assertNotRegex(output
, 'NO-CARRIER')
4468 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4476 def test_bridge_vlan(self
):
4477 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4478 '26-bridge.netdev', '26-bridge-vlan-master.network',
4481 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4483 output
= check_output('bridge vlan show dev test1')
4485 # check if the default VID is removed
4486 self
.assertNotIn('1 Egress Untagged', output
)
4487 for i
in range(1000, 3000):
4489 self
.assertIn(f
'{i} PVID', output
)
4490 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4491 self
.assertIn(f
'{i} Egress Untagged', output
)
4492 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4493 self
.assertIn(f
'{i}', output
)
4495 self
.assertNotIn(f
'{i}', output
)
4497 output
= check_output('bridge vlan show dev bridge99')
4499 # check if the default VID is removed
4500 self
.assertNotIn('1 Egress Untagged', output
)
4501 for i
in range(1000, 3000):
4503 self
.assertIn(f
'{i} PVID', output
)
4504 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4505 self
.assertIn(f
'{i} Egress Untagged', output
)
4506 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4507 self
.assertIn(f
'{i}', output
)
4509 self
.assertNotIn(f
'{i}', output
)
4512 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4513 '26-bridge-vlan-master.network.d/10-override.conf')
4515 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4517 output
= check_output('bridge vlan show dev test1')
4519 for i
in range(1000, 3000):
4521 self
.assertIn(f
'{i} PVID', output
)
4522 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4523 self
.assertIn(f
'{i} Egress Untagged', output
)
4524 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4525 self
.assertIn(f
'{i}', output
)
4527 self
.assertNotIn(f
'{i}', output
)
4529 output
= check_output('bridge vlan show dev bridge99')
4531 for i
in range(1000, 3000):
4533 self
.assertIn(f
'{i} PVID', output
)
4534 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4535 self
.assertIn(f
'{i} Egress Untagged', output
)
4536 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4537 self
.assertIn(f
'{i}', output
)
4539 self
.assertNotIn(f
'{i}', output
)
4541 # Remove several vlan IDs
4542 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4543 '26-bridge-vlan-master.network.d/20-override.conf')
4545 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4547 output
= check_output('bridge vlan show dev test1')
4549 for i
in range(1000, 3000):
4551 self
.assertIn(f
'{i} PVID', output
)
4552 elif i
in range(2012, 2016):
4553 self
.assertIn(f
'{i} Egress Untagged', output
)
4554 elif i
in range(2008, 2014):
4555 self
.assertIn(f
'{i}', output
)
4557 self
.assertNotIn(f
'{i}', output
)
4559 output
= check_output('bridge vlan show dev bridge99')
4561 for i
in range(1000, 3000):
4563 self
.assertIn(f
'{i} PVID', output
)
4564 elif i
in range(2022, 2026):
4565 self
.assertIn(f
'{i} Egress Untagged', output
)
4566 elif i
in range(2018, 2024):
4567 self
.assertIn(f
'{i}', output
)
4569 self
.assertNotIn(f
'{i}', output
)
4571 # Remove all vlan IDs
4572 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4573 '26-bridge-vlan-master.network.d/30-override.conf')
4575 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4577 output
= check_output('bridge vlan show dev test1')
4579 self
.assertNotIn('PVID', output
)
4580 for i
in range(1000, 3000):
4581 self
.assertNotIn(f
'{i}', output
)
4583 output
= check_output('bridge vlan show dev bridge99')
4585 self
.assertNotIn('PVID', output
)
4586 for i
in range(1000, 3000):
4587 self
.assertNotIn(f
'{i}', output
)
4589 def test_bridge_vlan_issue_20373(self
):
4590 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4591 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4592 '21-vlan.netdev', '21-vlan.network')
4594 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4596 output
= check_output('bridge vlan show dev test1')
4598 self
.assertIn('100 PVID Egress Untagged', output
)
4599 self
.assertIn('560', output
)
4600 self
.assertIn('600', output
)
4602 output
= check_output('bridge vlan show dev bridge99')
4604 self
.assertIn('1 PVID Egress Untagged', output
)
4605 self
.assertIn('100', output
)
4606 self
.assertIn('600', output
)
4608 def test_bridge_mdb(self
):
4609 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4610 '26-bridge.netdev', '26-bridge-mdb-master.network')
4612 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4614 output
= check_output('bridge mdb show dev bridge99')
4616 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4617 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4619 # Old kernel may not support bridge MDB entries on bridge master
4620 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4621 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4622 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4624 def test_bridge_keep_master(self
):
4625 check_output('ip link add bridge99 type bridge')
4626 check_output('ip link set bridge99 up')
4627 check_output('ip link add dummy98 type dummy')
4628 check_output('ip link set dummy98 master bridge99')
4630 copy_network_unit('23-keep-master.network')
4632 self
.wait_online(['dummy98:enslaved'])
4634 output
= check_output('ip -d link show dummy98')
4636 self
.assertRegex(output
, 'master bridge99')
4637 self
.assertRegex(output
, 'bridge')
4639 output
= check_output('bridge -d link show dummy98')
4641 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4642 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4643 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4644 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4645 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4646 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4647 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4648 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4649 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4650 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4651 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4652 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4654 def test_bridge_property(self
):
4655 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4656 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4657 '25-bridge99.network')
4659 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4661 output
= check_output('ip -d link show bridge99')
4663 self
.assertIn('mtu 9000 ', output
)
4665 output
= check_output('ip -d link show test1')
4667 self
.assertIn('master bridge99 ', output
)
4668 self
.assertIn('bridge_slave', output
)
4669 self
.assertIn('mtu 9000 ', output
)
4671 output
= check_output('ip -d link show dummy98')
4673 self
.assertIn('master bridge99 ', output
)
4674 self
.assertIn('bridge_slave', output
)
4675 self
.assertIn('mtu 9000 ', output
)
4677 output
= check_output('ip addr show bridge99')
4679 self
.assertIn('192.168.0.15/24', output
)
4681 output
= check_output('bridge -d link show dummy98')
4683 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4684 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4685 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4686 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4687 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4688 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4689 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4690 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4691 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4692 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4693 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4694 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4695 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4697 output
= check_output('bridge -d link show test1')
4699 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4701 check_output('ip address add 192.168.0.16/24 dev bridge99')
4702 output
= check_output('ip addr show bridge99')
4704 self
.assertIn('192.168.0.16/24', output
)
4707 print('### ip -6 route list table all dev bridge99')
4708 output
= check_output('ip -6 route list table all dev bridge99')
4710 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4712 remove_link('test1')
4713 self
.wait_operstate('bridge99', 'routable')
4715 output
= check_output('ip -d link show bridge99')
4717 self
.assertIn('mtu 9000 ', output
)
4719 output
= check_output('ip -d link show dummy98')
4721 self
.assertIn('master bridge99 ', output
)
4722 self
.assertIn('bridge_slave', output
)
4723 self
.assertIn('mtu 9000 ', output
)
4725 remove_link('dummy98')
4726 self
.wait_operstate('bridge99', 'no-carrier')
4728 output
= check_output('ip -d link show bridge99')
4730 # When no carrier, the kernel may reset the MTU
4731 self
.assertIn('NO-CARRIER', output
)
4733 output
= check_output('ip address show bridge99')
4735 self
.assertNotIn('192.168.0.15/24', output
)
4736 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4738 print('### ip -6 route list table all dev bridge99')
4739 output
= check_output('ip -6 route list table all dev bridge99')
4741 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4743 check_output('ip link add dummy98 type dummy')
4744 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4746 output
= check_output('ip -d link show bridge99')
4748 self
.assertIn('mtu 9000 ', output
)
4750 output
= check_output('ip -d link show dummy98')
4752 self
.assertIn('master bridge99 ', output
)
4753 self
.assertIn('bridge_slave', output
)
4754 self
.assertIn('mtu 9000 ', output
)
4756 def test_bridge_configure_without_carrier(self
):
4757 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4761 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4762 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4763 with self
.subTest(test
=test
):
4764 if test
== 'no-slave':
4765 # bridge has no slaves; it's up but *might* not have carrier
4766 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4767 # due to a bug in the kernel, newly-created bridges are brought up
4768 # *with* carrier, unless they have had any setting changed; e.g.
4769 # their mac set, priority set, etc. Then, they will lose carrier
4770 # as soon as a (down) slave interface is added, and regain carrier
4771 # again once the slave interface is brought up.
4772 #self.check_link_attr('bridge99', 'carrier', '0')
4773 elif test
== 'add-slave':
4774 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4775 self
.check_link_attr('test1', 'operstate', 'down')
4776 check_output('ip link set dev test1 master bridge99')
4777 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4778 self
.check_link_attr('bridge99', 'carrier', '0')
4779 elif test
== 'slave-up':
4780 # bring up slave, which will have carrier; bridge gains carrier
4781 check_output('ip link set dev test1 up')
4782 self
.wait_online(['bridge99:routable'])
4783 self
.check_link_attr('bridge99', 'carrier', '1')
4784 elif test
== 'slave-no-carrier':
4785 # drop slave carrier; bridge loses carrier
4786 check_output('ip link set dev test1 carrier off')
4787 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4788 self
.check_link_attr('bridge99', 'carrier', '0')
4789 elif test
== 'slave-carrier':
4790 # restore slave carrier; bridge gains carrier
4791 check_output('ip link set dev test1 carrier on')
4792 self
.wait_online(['bridge99:routable'])
4793 self
.check_link_attr('bridge99', 'carrier', '1')
4794 elif test
== 'slave-down':
4795 # bring down slave; bridge loses carrier
4796 check_output('ip link set dev test1 down')
4797 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4798 self
.check_link_attr('bridge99', 'carrier', '0')
4800 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
4801 self
.assertRegex(output
, '10.1.2.3')
4802 self
.assertRegex(output
, '10.1.2.1')
4804 def test_bridge_ignore_carrier_loss(self
):
4805 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4806 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4807 '25-bridge99-ignore-carrier-loss.network')
4809 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4811 check_output('ip address add 192.168.0.16/24 dev bridge99')
4812 remove_link('test1', 'dummy98')
4815 output
= check_output('ip address show bridge99')
4817 self
.assertRegex(output
, 'NO-CARRIER')
4818 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4819 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
4821 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
4822 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4823 '25-bridge99-ignore-carrier-loss.network')
4825 self
.wait_online(['bridge99:no-carrier'])
4827 for trial
in range(4):
4828 check_output('ip link add dummy98 type dummy')
4829 check_output('ip link set dummy98 up')
4831 remove_link('dummy98')
4833 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4835 output
= check_output('ip address show bridge99')
4837 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4839 output
= check_output('ip rule list table 100')
4841 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
4843 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
4851 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4852 def test_sriov(self
):
4853 copy_network_unit('25-default.link', '25-sriov.network')
4855 call('modprobe netdevsim')
4857 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4860 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
4864 self
.wait_online(['eni99np1:routable'])
4866 output
= check_output('ip link show dev eni99np1')
4868 self
.assertRegex(output
,
4869 '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 *'
4870 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4871 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4874 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4875 def test_sriov_udev(self
):
4876 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
4878 call('modprobe netdevsim')
4880 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4884 self
.wait_online(['eni99np1:routable'])
4886 # the name eni99np1 may be an alternative name.
4887 ifname
= link_resolve('eni99np1')
4889 output
= check_output('ip link show dev eni99np1')
4891 self
.assertRegex(output
,
4892 '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 *'
4893 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4894 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4896 self
.assertNotIn('vf 3', output
)
4897 self
.assertNotIn('vf 4', output
)
4899 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4900 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
4903 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4905 output
= check_output('ip link show dev eni99np1')
4907 self
.assertRegex(output
,
4908 '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 *'
4909 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4910 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4913 self
.assertNotIn('vf 4', output
)
4915 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4916 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4919 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4921 output
= check_output('ip link show dev eni99np1')
4923 self
.assertRegex(output
,
4924 '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 *'
4925 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4926 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4929 self
.assertNotIn('vf 4', output
)
4931 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4932 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
4935 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4937 output
= check_output('ip link show dev eni99np1')
4939 self
.assertRegex(output
,
4940 '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 *'
4941 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
4943 self
.assertNotIn('vf 2', output
)
4944 self
.assertNotIn('vf 3', output
)
4945 self
.assertNotIn('vf 4', output
)
4947 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4948 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4951 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4953 output
= check_output('ip link show dev eni99np1')
4955 self
.assertRegex(output
,
4956 '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 *'
4957 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4958 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4960 self
.assertNotIn('vf 3', output
)
4961 self
.assertNotIn('vf 4', output
)
4963 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
4971 def test_lldp(self
):
4972 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
4974 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
4976 for trial
in range(10):
4980 output
= check_output(*networkctl_cmd
, 'lldp', env
=env
)
4982 if re
.search(r
'veth99 .* veth-peer', output
):
4987 class NetworkdRATests(unittest
.TestCase
, Utilities
):
4995 def test_ipv6_prefix_delegation(self
):
4996 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
4997 self
.setup_nftset('addr6', 'ipv6_addr')
4998 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
4999 self
.setup_nftset('ifindex', 'iface_index')
5001 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5003 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
5005 self
.assertRegex(output
, 'fe80::')
5006 self
.assertRegex(output
, '2002:da8:1::1')
5008 output
= check_output(*resolvectl_cmd
, 'domain', 'veth99', env
=env
)
5010 self
.assertIn('hogehoge.test', output
)
5012 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5014 self
.assertRegex(output
, '2002:da8:1:0')
5016 self
.check_netlabel('veth99', '2002:da8:1::/64')
5017 self
.check_netlabel('veth99', '2002:da8:2::/64')
5019 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5020 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5021 self
.check_nftset('network6', '2002:da8:1::/64')
5022 self
.check_nftset('network6', '2002:da8:2::/64')
5023 self
.check_nftset('ifindex', 'veth99')
5025 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5027 def test_ipv6_token_static(self
):
5028 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5030 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5032 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5034 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5035 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5036 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5037 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5039 def test_ipv6_token_prefixstable(self
):
5040 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5042 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5044 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5046 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5047 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5049 def test_ipv6_token_prefixstable_without_address(self
):
5050 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5052 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5054 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5056 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5057 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5059 def test_router_preference(self
):
5060 copy_network_unit('25-veth-client.netdev',
5061 '25-veth-router-high.netdev',
5062 '25-veth-router-low.netdev',
5064 '25-veth-bridge.network',
5065 '25-veth-client.network',
5066 '25-veth-router-high.network',
5067 '25-veth-router-low.network',
5068 '25-bridge99.network')
5070 self
.wait_online(['client-p:enslaved',
5071 'router-high:degraded', 'router-high-p:enslaved',
5072 'router-low:degraded', 'router-low-p:enslaved',
5073 'bridge99:routable'])
5075 networkctl_reconfigure('client')
5076 self
.wait_online(['client:routable'])
5078 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5079 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5080 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5081 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5083 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5085 self
.assertIn('pref high', output
)
5086 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5088 self
.assertIn('pref low', output
)
5090 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5091 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5094 self
.wait_online(['client:routable'])
5096 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5097 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5098 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5099 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5101 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5103 self
.assertIn('pref high', output
)
5104 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5106 self
.assertIn('pref low', output
)
5108 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5109 def test_captive_portal(self
):
5110 copy_network_unit('25-veth-client.netdev',
5111 '25-veth-router-captive.netdev',
5113 '25-veth-client-captive.network',
5114 '25-veth-router-captive.network',
5115 '25-veth-bridge-captive.network',
5116 '25-bridge99.network')
5118 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5119 'router-captive:degraded', 'router-captivep:enslaved'])
5121 start_radvd(config_file
='captive-portal.conf')
5122 networkctl_reconfigure('client')
5123 self
.wait_online(['client:routable'])
5125 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5126 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5128 self
.assertIn('Captive Portal: http://systemd.io', output
)
5130 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5131 def test_invalid_captive_portal(self
):
5132 def radvd_write_config(captive_portal_uri
):
5133 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5134 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5136 captive_portal_uris
= [
5137 "42ěščěškd ěšč ě s",
5142 copy_network_unit('25-veth-client.netdev',
5143 '25-veth-router-captive.netdev',
5145 '25-veth-client-captive.network',
5146 '25-veth-router-captive.network',
5147 '25-veth-bridge-captive.network',
5148 '25-bridge99.network')
5150 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5151 'router-captive:degraded', 'router-captivep:enslaved'])
5153 for uri
in captive_portal_uris
:
5154 print(f
"Captive portal: {uri}")
5155 radvd_write_config(uri
)
5157 start_radvd(config_file
='bogus-captive-portal.conf')
5158 networkctl_reconfigure('client')
5159 self
.wait_online(['client:routable'])
5161 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5162 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5164 self
.assertNotIn('Captive Portal:', output
)
5166 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5174 def test_dhcp_server(self
):
5175 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5177 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5179 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5181 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5182 self
.assertIn('Gateway: 192.168.5.3', output
)
5183 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5184 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5186 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5187 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5189 def test_dhcp_server_null_server_address(self
):
5190 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5192 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5194 output
= check_output('ip --json address show dev veth-peer')
5195 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5196 print(server_address
)
5198 output
= check_output('ip --json address show dev veth99')
5199 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5200 print(client_address
)
5202 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5204 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5205 self
.assertIn(f
'Gateway: {server_address}', output
)
5206 self
.assertIn(f
'DNS: {server_address}', output
)
5207 self
.assertIn(f
'NTP: {server_address}', output
)
5209 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5210 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5212 def test_dhcp_server_with_uplink(self
):
5213 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5214 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5216 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5218 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5220 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5221 self
.assertIn('Gateway: 192.168.5.3', output
)
5222 self
.assertIn('DNS: 192.168.5.1', output
)
5223 self
.assertIn('NTP: 192.168.5.1', output
)
5225 def test_emit_router_timezone(self
):
5226 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5228 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5230 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5232 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5233 self
.assertIn('Gateway: 192.168.5.1', output
)
5234 self
.assertIn('Time Zone: Europe/Berlin', output
)
5236 def test_dhcp_server_static_lease(self
):
5237 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5239 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5241 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5243 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5244 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5246 def test_dhcp_server_static_lease_default_client_id(self
):
5247 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5249 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5251 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5253 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5254 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5256 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5264 def test_relay_agent(self
):
5265 copy_network_unit('25-agent-veth-client.netdev',
5266 '25-agent-veth-server.netdev',
5267 '25-agent-client.network',
5268 '25-agent-server.network',
5269 '25-agent-client-peer.network',
5270 '25-agent-server-peer.network')
5273 self
.wait_online(['client:routable'])
5275 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'client', env
=env
)
5277 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5279 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5287 def test_dhcp_client_ipv6_only(self
):
5288 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5291 self
.wait_online(['veth-peer:carrier'])
5293 # information request mode
5294 # The name ipv6-only option may not be supported by older dnsmasq
5295 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5296 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5297 '--dhcp-option=option6:dns-server,[2600::ee]',
5298 '--dhcp-option=option6:ntp-server,[2600::ff]',
5299 ra_mode
='ra-stateless')
5300 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5302 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5303 # Let's wait for the expected DNS server being listed in the state file.
5304 for _
in range(100):
5305 output
= read_link_state_file('veth99')
5306 if 'DNS=2600::ee' in output
:
5310 # Check link state file
5311 print('## link state file')
5312 output
= read_link_state_file('veth99')
5314 self
.assertIn('DNS=2600::ee', output
)
5315 self
.assertIn('NTP=2600::ff', output
)
5317 # Check manager state file
5318 print('## manager state file')
5319 output
= read_manager_state_file()
5321 self
.assertRegex(output
, 'DNS=.*2600::ee')
5322 self
.assertRegex(output
, 'NTP=.*2600::ff')
5324 print('## dnsmasq log')
5325 output
= read_dnsmasq_log_file()
5327 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5328 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5329 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5330 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5331 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5334 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5339 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5340 '--dhcp-option=option6:dns-server,[2600::ee]',
5341 '--dhcp-option=option6:ntp-server,[2600::ff]')
5342 networkctl_reconfigure('veth99')
5343 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5346 output
= check_output('ip address show dev veth99 scope global')
5348 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5349 self
.assertNotIn('192.168.5', output
)
5351 # checking semi-static route
5352 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5354 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5356 # Confirm that ipv6 token is not set in the kernel
5357 output
= check_output('ip token show dev veth99')
5359 self
.assertRegex(output
, 'token :: dev veth99')
5361 # Make manager and link state file updated
5362 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5364 # Check link state file
5365 print('## link state file')
5366 output
= read_link_state_file('veth99')
5368 self
.assertIn('DNS=2600::ee', output
)
5369 self
.assertIn('NTP=2600::ff', output
)
5371 # Check manager state file
5372 print('## manager state file')
5373 output
= read_manager_state_file()
5375 self
.assertRegex(output
, 'DNS=.*2600::ee')
5376 self
.assertRegex(output
, 'NTP=.*2600::ff')
5378 print('## dnsmasq log')
5379 output
= read_dnsmasq_log_file()
5381 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5382 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5383 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5384 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5385 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5386 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5389 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5392 # Testing without rapid commit support
5393 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5394 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5397 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5398 '--dhcp-option=option6:dns-server,[2600::ee]',
5399 '--dhcp-option=option6:ntp-server,[2600::ff]')
5402 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5405 output
= check_output('ip address show dev veth99 scope global')
5407 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5408 self
.assertNotIn('192.168.5', output
)
5410 # checking semi-static route
5411 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5413 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5415 # Make manager and link state file updated
5416 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5418 # Check link state file
5419 print('## link state file')
5420 output
= read_link_state_file('veth99')
5422 self
.assertIn('DNS=2600::ee', output
)
5423 self
.assertIn('NTP=2600::ff', output
)
5425 # Check manager state file
5426 print('## manager state file')
5427 output
= read_manager_state_file()
5429 self
.assertRegex(output
, 'DNS=.*2600::ee')
5430 self
.assertRegex(output
, 'NTP=.*2600::ff')
5432 print('## dnsmasq log')
5433 output
= read_dnsmasq_log_file()
5435 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5436 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5437 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5438 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5439 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5440 self
.assertNotIn('rapid-commit', output
)
5443 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5446 def test_dhcp_client_ipv6_dbus_status(self
):
5447 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5449 self
.wait_online(['veth-peer:carrier'])
5451 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5452 # bit set) has yet been received and the configuration does not include WithoutRA=true
5453 state
= get_dhcp6_client_state('veth99')
5454 print(f
"DHCPv6 client state = {state}")
5455 self
.assertEqual(state
, 'stopped')
5457 state
= get_dhcp4_client_state('veth99')
5458 print(f
"DHCPv4 client state = {state}")
5459 self
.assertEqual(state
, 'selecting')
5461 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5462 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5464 state
= get_dhcp6_client_state('veth99')
5465 print(f
"DHCPv6 client state = {state}")
5466 self
.assertEqual(state
, 'bound')
5468 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5469 for _
in range(100):
5470 state
= get_dhcp4_client_state('veth99')
5471 if state
== 'stopped':
5475 print(f
"DHCPv4 client state = {state}")
5476 self
.assertEqual(state
, 'stopped')
5478 # restart dnsmasq to clear log
5480 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5482 # Test renew command
5483 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5484 check_output(*networkctl_cmd
, 'renew', 'veth99', env
=env
)
5486 for _
in range(100):
5487 state
= get_dhcp4_client_state('veth99')
5488 if state
== 'stopped':
5492 print(f
"DHCPv4 client state = {state}")
5493 self
.assertEqual(state
, 'stopped')
5495 print('## dnsmasq log')
5496 output
= read_dnsmasq_log_file()
5498 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5499 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5500 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5501 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5503 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5504 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5507 self
.wait_online(['veth-peer:carrier'])
5509 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5512 output
= check_output('ip address show dev veth99 scope global')
5514 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5515 self
.assertNotIn('192.168.5', output
)
5517 print('## dnsmasq log')
5518 output
= read_dnsmasq_log_file()
5520 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5521 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5522 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5523 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5524 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5526 def test_dhcp_client_ipv4_only(self
):
5527 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5529 self
.setup_nftset('addr4', 'ipv4_addr')
5530 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5531 self
.setup_nftset('ifindex', 'iface_index')
5534 self
.wait_online(['veth-peer:carrier'])
5535 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5536 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5537 '--dhcp-option=option:domain-search,example.com',
5538 '--dhcp-alternate-port=67,5555',
5539 ipv4_range
='192.168.5.110,192.168.5.119')
5540 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5541 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5543 print('## ip address show dev veth99 scope global')
5544 output
= check_output('ip address show dev veth99 scope global')
5546 self
.assertIn('mtu 1492', output
)
5547 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5548 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')
5549 self
.assertNotIn('2600::', output
)
5551 output
= check_output('ip -4 --json address show dev veth99')
5552 for i
in json
.loads(output
)[0]['addr_info']:
5553 if i
['label'] == 'test-label':
5554 address1
= i
['local']
5557 self
.assertFalse(True)
5559 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5561 print('## ip route show table main dev veth99')
5562 output
= check_output('ip route show table main dev veth99')
5564 # no DHCP routes assigned to the main table
5565 self
.assertNotIn('proto dhcp', output
)
5567 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5568 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5569 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5570 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5572 print('## ip route show table 211 dev veth99')
5573 output
= check_output('ip route show table 211 dev veth99')
5575 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5576 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5577 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5578 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5579 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5580 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5582 print('## link state file')
5583 output
= read_link_state_file('veth99')
5585 # checking DNS server, SIP server, and Domains
5586 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5587 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5588 self
.assertIn('DOMAINS=example.com', output
)
5591 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5592 j
= json
.loads(output
)
5594 self
.assertEqual(len(j
['DNS']), 2)
5597 self
.assertEqual(i
['Family'], 2)
5598 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5599 self
.assertRegex(a
, '^192.168.5.[67]$')
5600 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5601 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5602 self
.assertEqual('192.168.5.1', a
)
5604 self
.assertEqual(len(j
['SIP']), 2)
5607 self
.assertEqual(i
['Family'], 2)
5608 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5609 self
.assertRegex(a
, '^192.168.5.2[12]$')
5610 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5611 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5612 self
.assertEqual('192.168.5.1', a
)
5614 print('## dnsmasq log')
5615 output
= read_dnsmasq_log_file()
5617 self
.assertIn('vendor class: FooBarVendorTest', output
)
5618 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5619 self
.assertIn('client provides name: test-hostname', output
)
5620 self
.assertIn('26:mtu', output
)
5622 # change address range, DNS servers, and Domains
5624 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5625 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5626 '--dhcp-option=option:domain-search,foo.example.com',
5627 '--dhcp-alternate-port=67,5555',
5628 ipv4_range
='192.168.5.120,192.168.5.129',)
5630 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5631 print('Wait for the DHCP lease to be expired')
5632 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5633 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5635 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5637 print('## ip address show dev veth99 scope global')
5638 output
= check_output('ip address show dev veth99 scope global')
5640 self
.assertIn('mtu 1492', output
)
5641 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5642 self
.assertNotIn(f
'{address1}', output
)
5643 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')
5644 self
.assertNotIn('2600::', output
)
5646 output
= check_output('ip -4 --json address show dev veth99')
5647 for i
in json
.loads(output
)[0]['addr_info']:
5648 if i
['label'] == 'test-label':
5649 address2
= i
['local']
5652 self
.assertFalse(True)
5654 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5656 print('## ip route show table main dev veth99')
5657 output
= check_output('ip route show table main dev veth99')
5659 # no DHCP routes assigned to the main table
5660 self
.assertNotIn('proto dhcp', output
)
5662 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5663 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5664 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5665 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5667 print('## ip route show table 211 dev veth99')
5668 output
= check_output('ip route show table 211 dev veth99')
5670 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5671 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5672 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5673 self
.assertNotIn('192.168.5.6', output
)
5674 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5675 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5676 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5678 print('## link state file')
5679 output
= read_link_state_file('veth99')
5681 # checking DNS server, SIP server, and Domains
5682 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5683 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5684 self
.assertIn('DOMAINS=foo.example.com', output
)
5687 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5688 j
= json
.loads(output
)
5690 self
.assertEqual(len(j
['DNS']), 3)
5693 self
.assertEqual(i
['Family'], 2)
5694 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5695 self
.assertRegex(a
, '^192.168.5.[178]$')
5696 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5697 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5698 self
.assertEqual('192.168.5.1', a
)
5700 self
.assertEqual(len(j
['SIP']), 2)
5703 self
.assertEqual(i
['Family'], 2)
5704 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5705 self
.assertRegex(a
, '^192.168.5.2[34]$')
5706 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5707 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5708 self
.assertEqual('192.168.5.1', a
)
5710 print('## dnsmasq log')
5711 output
= read_dnsmasq_log_file()
5713 self
.assertIn('vendor class: FooBarVendorTest', output
)
5714 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5715 self
.assertIn('client provides name: test-hostname', output
)
5716 self
.assertIn('26:mtu', output
)
5718 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5720 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5721 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5722 self
.check_nftset('ifindex', 'veth99')
5724 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5726 def test_dhcp_client_ipv4_dbus_status(self
):
5727 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5729 self
.wait_online(['veth-peer:carrier'])
5731 state
= get_dhcp4_client_state('veth99')
5732 print(f
"State = {state}")
5733 self
.assertEqual(state
, 'rebooting')
5735 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5736 '--dhcp-option=option:domain-search,example.com',
5737 '--dhcp-alternate-port=67,5555',
5738 ipv4_range
='192.168.5.110,192.168.5.119')
5739 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5740 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5742 state
= get_dhcp4_client_state('veth99')
5743 print(f
"State = {state}")
5744 self
.assertEqual(state
, 'bound')
5746 def test_dhcp_client_allow_list(self
):
5747 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5750 self
.wait_online(['veth-peer:carrier'])
5751 since
= datetime
.datetime
.now()
5754 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5756 if expect
in read_networkd_log(since
=since
):
5762 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
5763 since
= datetime
.datetime
.now()
5766 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5768 if expect
in read_networkd_log(since
=since
):
5774 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
5775 since
= datetime
.datetime
.now()
5778 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
5780 if expect
in read_networkd_log(since
=since
):
5786 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
5787 def test_dhcp_client_rapid_commit(self
):
5788 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5790 self
.wait_online(['veth-peer:carrier'])
5792 start_dnsmasq('--dhcp-rapid-commit')
5793 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5794 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5796 state
= get_dhcp4_client_state('veth99')
5797 print(f
"DHCPv4 client state = {state}")
5798 self
.assertEqual(state
, 'bound')
5800 output
= read_dnsmasq_log_file()
5801 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
5802 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
5803 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5804 self
.assertIn('DHCPACK(veth-peer)', output
)
5806 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
5807 copy_network_unit('25-veth.netdev',
5808 '25-dhcp-server-ipv6-only-mode.network',
5809 '25-dhcp-client-ipv6-only-mode.network')
5811 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
5812 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5814 state
= get_dhcp4_client_state('veth99')
5815 print(f
"State = {state}")
5816 self
.assertEqual(state
, 'bound')
5818 def test_dhcp_client_ipv4_use_routes_gateway(self
):
5820 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
5826 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
5827 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
5828 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
5830 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
5831 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
5832 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
5833 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
5834 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
5835 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
5836 copy_network_unit(*testunits
, copy_dropins
=False)
5839 self
.wait_online(['veth-peer:carrier'])
5840 additional_options
= [
5841 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
5842 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
5843 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
5846 additional_options
+= [
5847 '--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'
5849 start_dnsmasq(*additional_options
)
5850 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5852 output
= check_output('ip -4 route show dev veth99')
5858 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5859 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5860 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5861 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5862 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5864 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5865 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5866 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5867 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5869 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5870 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5871 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5872 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5873 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5874 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5875 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5876 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5879 if use_gateway
and (not classless
or not use_routes
):
5880 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5882 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5884 # Check route to gateway
5885 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
5886 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5888 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5890 # Check RoutesToDNS= and RoutesToNTP=
5891 if dns_and_ntp_routes
:
5892 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5893 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5896 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5897 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5899 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5900 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5902 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5903 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5905 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5906 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5907 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5908 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5910 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
5913 def test_dhcp_client_settings_anonymize(self
):
5914 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
5916 self
.wait_online(['veth-peer:carrier'])
5918 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5920 print('## dnsmasq log')
5921 output
= read_dnsmasq_log_file()
5923 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
5924 self
.assertNotIn('test-hostname', output
)
5925 self
.assertNotIn('26:mtu', output
)
5927 def test_dhcp_keep_configuration_dhcp(self
):
5928 copy_network_unit('25-veth.netdev',
5929 '25-dhcp-server-veth-peer.network',
5930 '25-dhcp-client-keep-configuration-dhcp.network')
5932 self
.wait_online(['veth-peer:carrier'])
5934 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5936 output
= check_output('ip address show dev veth99 scope global')
5938 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5939 'valid_lft forever preferred_lft forever')
5941 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
5944 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5945 print('Wait for the DHCP lease to be expired')
5948 # The lease address should be kept after the lease expired
5949 output
= check_output('ip address show dev veth99 scope global')
5951 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5952 'valid_lft forever preferred_lft forever')
5956 # The lease address should be kept after networkd stopped
5957 output
= check_output('ip address show dev veth99 scope global')
5959 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5960 'valid_lft forever preferred_lft forever')
5962 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
5963 f
.write('[Network]\nDHCP=no\n')
5966 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5968 # Still the lease address should be kept after networkd restarted
5969 output
= check_output('ip address show dev veth99 scope global')
5971 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5972 'valid_lft forever preferred_lft forever')
5974 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
5975 copy_network_unit('25-veth.netdev',
5976 '25-dhcp-server-veth-peer.network',
5977 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
5979 self
.wait_online(['veth-peer:carrier'])
5981 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5983 output
= check_output('ip address show dev veth99 scope global')
5985 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5990 output
= check_output('ip address show dev veth99 scope global')
5992 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5995 self
.wait_online(['veth-peer:routable'])
5997 output
= check_output('ip address show dev veth99 scope global')
5999 self
.assertNotIn('192.168.5.', output
)
6001 def test_dhcp_client_reuse_address_as_static(self
):
6002 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6004 self
.wait_online(['veth-peer:carrier'])
6006 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6008 # link become 'routable' when at least one protocol provide an valid address.
6009 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6010 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6012 output
= check_output('ip address show dev veth99 scope global')
6013 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6014 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6015 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6016 print(static_network
)
6018 remove_network_unit('25-dhcp-client.network')
6020 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6021 f
.write(static_network
)
6024 self
.wait_online(['veth99:routable'])
6026 output
= check_output('ip -4 address show dev veth99 scope global')
6028 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6029 'valid_lft forever preferred_lft forever')
6031 output
= check_output('ip -6 address show dev veth99 scope global')
6033 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6034 'valid_lft forever preferred_lft forever')
6036 @expectedFailureIfModuleIsNotAvailable('vrf')
6037 def test_dhcp_client_vrf(self
):
6038 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6039 '25-vrf.netdev', '25-vrf.network')
6041 self
.wait_online(['veth-peer:carrier'])
6043 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
6045 # link become 'routable' when at least one protocol provide an valid address.
6046 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6047 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6049 print('## ip -d link show dev vrf99')
6050 output
= check_output('ip -d link show dev vrf99')
6052 self
.assertRegex(output
, 'vrf table 42')
6054 print('## ip address show vrf vrf99')
6055 output
= check_output('ip address show vrf vrf99')
6057 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6058 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6059 self
.assertRegex(output
, 'inet6 .* scope link')
6061 print('## ip address show dev veth99')
6062 output
= check_output('ip address show dev veth99')
6064 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6065 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6066 self
.assertRegex(output
, 'inet6 .* scope link')
6068 print('## ip route show vrf vrf99')
6069 output
= check_output('ip route show vrf vrf99')
6071 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6072 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6073 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6075 print('## ip route show table main dev veth99')
6076 output
= check_output('ip route show table main dev veth99')
6078 self
.assertEqual(output
, '')
6080 def test_dhcp_client_gateway_onlink_implicit(self
):
6081 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6082 '25-dhcp-client-gateway-onlink-implicit.network')
6084 self
.wait_online(['veth-peer:carrier'])
6086 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6088 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
6090 self
.assertRegex(output
, '192.168.5')
6092 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6094 self
.assertRegex(output
, 'onlink')
6095 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6097 self
.assertRegex(output
, 'onlink')
6099 def test_dhcp_client_with_ipv4ll(self
):
6100 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6101 '25-dhcp-client-with-ipv4ll.network')
6103 # we need to increase timeout above default, as this will need to wait for
6104 # systemd-networkd to get the dhcpv4 transient failure event
6105 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6107 output
= check_output('ip -4 address show dev veth99')
6109 self
.assertNotIn('192.168.5.', output
)
6110 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6113 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6114 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6115 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')
6116 self
.wait_online(['veth99:routable'])
6118 output
= check_output('ip -4 address show dev veth99')
6120 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6121 self
.assertNotIn('169.254.', output
)
6122 self
.assertNotIn('scope link', output
)
6125 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6126 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)
6127 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6129 output
= check_output('ip -4 address show dev veth99')
6131 self
.assertNotIn('192.168.5.', output
)
6132 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6134 def test_dhcp_client_use_dns(self
):
6135 def check(self
, ipv4
, ipv6
):
6136 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6137 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6138 f
.write('[DHCPv4]\nUseDNS=')
6139 f
.write('yes' if ipv4
else 'no')
6140 f
.write('\n[DHCPv6]\nUseDNS=')
6141 f
.write('yes' if ipv6
else 'no')
6142 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6145 self
.wait_online(['veth99:routable'])
6147 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6148 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6149 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6151 # make resolved re-read the link state file
6152 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
6154 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
6157 self
.assertIn('192.168.5.1', output
)
6159 self
.assertNotIn('192.168.5.1', output
)
6161 self
.assertIn('2600::1', output
)
6163 self
.assertNotIn('2600::1', output
)
6165 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6168 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6171 self
.wait_online(['veth-peer:carrier'])
6172 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6173 '--dhcp-option=option6:dns-server,[2600::1]')
6175 check(self
, True, True)
6176 check(self
, True, False)
6177 check(self
, False, True)
6178 check(self
, False, False)
6180 def test_dhcp_client_use_captive_portal(self
):
6181 def check(self
, ipv4
, ipv6
):
6182 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6183 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6184 f
.write('[DHCPv4]\nUseCaptivePortal=')
6185 f
.write('yes' if ipv4
else 'no')
6186 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6187 f
.write('yes' if ipv6
else 'no')
6188 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6191 self
.wait_online(['veth99:routable'])
6193 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6194 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6195 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6197 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6200 self
.assertIn('Captive Portal: http://systemd.io', output
)
6202 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6204 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6207 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6210 self
.wait_online(['veth-peer:carrier'])
6211 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6212 '--dhcp-option=option6:103,http://systemd.io')
6214 check(self
, True, True)
6215 check(self
, True, False)
6216 check(self
, False, True)
6217 check(self
, False, False)
6219 def test_dhcp_client_reject_captive_portal(self
):
6220 def check(self
, ipv4
, ipv6
):
6221 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6222 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6223 f
.write('[DHCPv4]\nUseCaptivePortal=')
6224 f
.write('yes' if ipv4
else 'no')
6225 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6226 f
.write('yes' if ipv6
else 'no')
6227 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6230 self
.wait_online(['veth99:routable'])
6232 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6233 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6234 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6236 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6238 self
.assertNotIn('Captive Portal: ', output
)
6239 self
.assertNotIn('invalid/url', output
)
6241 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6244 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6247 self
.wait_online(['veth-peer:carrier'])
6248 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6249 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6250 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6252 check(self
, True, True)
6253 check(self
, True, False)
6254 check(self
, False, True)
6255 check(self
, False, False)
6257 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6265 def test_dhcp6pd(self
):
6266 def get_dhcp6_prefix(link
):
6267 description
= get_link_description(link
)
6269 self
.assertIn('DHCPv6Client', description
.keys())
6270 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6272 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6276 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6277 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6278 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6279 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6280 '25-dhcp-pd-downstream-dummy97.network',
6281 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6282 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6285 self
.setup_nftset('addr6', 'ipv6_addr')
6286 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6287 self
.setup_nftset('ifindex', 'iface_index')
6290 self
.wait_online(['veth-peer:routable'])
6291 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6292 self
.wait_online(['veth99:degraded'])
6294 # First, test UseAddress=no and Assign=no (issue #29979).
6295 # Note, due to the bug #29701, this test must be done at first.
6296 print('### ip -6 address show dev veth99 scope global')
6297 output
= check_output('ip -6 address show dev veth99 scope global')
6299 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6301 # Check DBus assigned prefix information to veth99
6302 prefixInfo
= get_dhcp6_prefix('veth99')
6304 self
.assertEqual(len(prefixInfo
), 1)
6305 prefixInfo
= prefixInfo
[0]
6307 self
.assertIn('Prefix', prefixInfo
.keys())
6308 self
.assertIn('PrefixLength', prefixInfo
.keys())
6309 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6310 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6312 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6313 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6314 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6315 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6317 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6319 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6320 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6322 print('### ip -6 address show dev veth-peer scope global')
6323 output
= check_output('ip -6 address show dev veth-peer scope global')
6325 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6329 # dummy97: 0x01 (The link will appear later)
6331 # dummy99: auto -> 0x02 (No address assignment)
6336 print('### ip -6 address show dev veth99 scope global')
6337 output
= check_output('ip -6 address show dev veth99 scope global')
6340 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6341 # address in IA_PD (Token=static)
6342 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6343 # address in IA_PD (Token=eui64)
6344 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6345 # address in IA_PD (temporary)
6346 # Note that the temporary addresses may appear after the link enters configured state
6347 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')
6349 print('### ip -6 address show dev test1 scope global')
6350 output
= check_output('ip -6 address show dev test1 scope global')
6352 # address in IA_PD (Token=static)
6353 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6354 # address in IA_PD (temporary)
6355 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')
6357 print('### ip -6 address show dev dummy98 scope global')
6358 output
= check_output('ip -6 address show dev dummy98 scope global')
6360 # address in IA_PD (Token=static)
6361 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6362 # address in IA_PD (temporary)
6363 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')
6365 print('### ip -6 address show dev dummy99 scope global')
6366 output
= check_output('ip -6 address show dev dummy99 scope global')
6369 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6371 print('### ip -6 address show dev veth97 scope global')
6372 output
= check_output('ip -6 address show dev veth97 scope global')
6374 # address in IA_PD (Token=static)
6375 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6376 # address in IA_PD (Token=eui64)
6377 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6378 # address in IA_PD (temporary)
6379 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')
6381 print('### ip -6 address show dev veth97-peer scope global')
6382 output
= check_output('ip -6 address show dev veth97-peer scope global')
6384 # NDisc address (Token=static)
6385 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6386 # NDisc address (Token=eui64)
6387 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6388 # NDisc address (temporary)
6389 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')
6391 print('### ip -6 address show dev veth98 scope global')
6392 output
= check_output('ip -6 address show dev veth98 scope global')
6394 # address in IA_PD (Token=static)
6395 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6396 # address in IA_PD (Token=eui64)
6397 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6398 # address in IA_PD (temporary)
6399 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')
6401 print('### ip -6 address show dev veth98-peer scope global')
6402 output
= check_output('ip -6 address show dev veth98-peer scope global')
6404 # NDisc address (Token=static)
6405 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6406 # NDisc address (Token=eui64)
6407 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6408 # NDisc address (temporary)
6409 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')
6411 print('### ip -6 route show type unreachable')
6412 output
= check_output('ip -6 route show type unreachable')
6414 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6416 print('### ip -6 route show dev veth99')
6417 output
= check_output('ip -6 route show dev veth99')
6419 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6421 print('### ip -6 route show dev test1')
6422 output
= check_output('ip -6 route show dev test1')
6424 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6426 print('### ip -6 route show dev dummy98')
6427 output
= check_output('ip -6 route show dev dummy98')
6429 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6431 print('### ip -6 route show dev dummy99')
6432 output
= check_output('ip -6 route show dev dummy99')
6434 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6436 print('### ip -6 route show dev veth97')
6437 output
= check_output('ip -6 route show dev veth97')
6439 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6441 print('### ip -6 route show dev veth97-peer')
6442 output
= check_output('ip -6 route show dev veth97-peer')
6444 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6446 print('### ip -6 route show dev veth98')
6447 output
= check_output('ip -6 route show dev veth98')
6449 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6451 print('### ip -6 route show dev veth98-peer')
6452 output
= check_output('ip -6 route show dev veth98-peer')
6454 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6456 # Test case for a downstream which appears later
6457 check_output('ip link add dummy97 type dummy')
6458 self
.wait_online(['dummy97:routable'])
6460 print('### ip -6 address show dev dummy97 scope global')
6461 output
= check_output('ip -6 address show dev dummy97 scope global')
6463 # address in IA_PD (Token=static)
6464 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6465 # address in IA_PD (temporary)
6466 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')
6468 print('### ip -6 route show dev dummy97')
6469 output
= check_output('ip -6 route show dev dummy97')
6471 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6473 # Test case for reconfigure
6474 networkctl_reconfigure('dummy98', 'dummy99')
6475 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6477 print('### ip -6 address show dev dummy98 scope global')
6478 output
= check_output('ip -6 address show dev dummy98 scope global')
6480 # address in IA_PD (Token=static)
6481 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6482 # address in IA_PD (temporary)
6483 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')
6485 print('### ip -6 address show dev dummy99 scope global')
6486 output
= check_output('ip -6 address show dev dummy99 scope global')
6489 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6491 print('### ip -6 route show dev dummy98')
6492 output
= check_output('ip -6 route show dev dummy98')
6494 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6496 print('### ip -6 route show dev dummy99')
6497 output
= check_output('ip -6 route show dev dummy99')
6499 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6501 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6503 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6504 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6505 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6506 self
.check_nftset('ifindex', 'dummy98')
6508 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6510 def verify_dhcp4_6rd(self
, tunnel_name
):
6511 print('### ip -4 address show dev veth-peer scope global')
6512 output
= check_output('ip -4 address show dev veth-peer scope global')
6514 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6518 # dummy97: 0x01 (The link will appear later)
6520 # dummy99: auto -> 0x0[23] (No address assignment)
6521 # 6rd-XXX: auto -> 0x0[23]
6526 print('### ip -4 address show dev veth99 scope global')
6527 output
= check_output('ip -4 address show dev veth99 scope global')
6529 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6531 print('### ip -6 address show dev veth99 scope global')
6532 output
= check_output('ip -6 address show dev veth99 scope global')
6534 # address in IA_PD (Token=static)
6535 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6536 # address in IA_PD (Token=eui64)
6537 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6538 # address in IA_PD (temporary)
6539 # Note that the temporary addresses may appear after the link enters configured state
6540 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')
6542 print('### ip -6 address show dev test1 scope global')
6543 output
= check_output('ip -6 address show dev test1 scope global')
6545 # address in IA_PD (Token=static)
6546 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6547 # address in IA_PD (temporary)
6548 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')
6550 print('### ip -6 address show dev dummy98 scope global')
6551 output
= check_output('ip -6 address show dev dummy98 scope global')
6553 # address in IA_PD (Token=static)
6554 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6555 # address in IA_PD (temporary)
6556 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')
6558 print('### ip -6 address show dev dummy99 scope global')
6559 output
= check_output('ip -6 address show dev dummy99 scope global')
6562 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6564 print('### ip -6 address show dev veth97 scope global')
6565 output
= check_output('ip -6 address show dev veth97 scope global')
6567 # address in IA_PD (Token=static)
6568 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6569 # address in IA_PD (Token=eui64)
6570 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6571 # address in IA_PD (temporary)
6572 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')
6574 print('### ip -6 address show dev veth97-peer scope global')
6575 output
= check_output('ip -6 address show dev veth97-peer scope global')
6577 # NDisc address (Token=static)
6578 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6579 # NDisc address (Token=eui64)
6580 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6581 # NDisc address (temporary)
6582 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')
6584 print('### ip -6 address show dev veth98 scope global')
6585 output
= check_output('ip -6 address show dev veth98 scope global')
6587 # address in IA_PD (Token=static)
6588 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6589 # address in IA_PD (Token=eui64)
6590 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6591 # address in IA_PD (temporary)
6592 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')
6594 print('### ip -6 address show dev veth98-peer scope global')
6595 output
= check_output('ip -6 address show dev veth98-peer scope global')
6597 # NDisc address (Token=static)
6598 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6599 # NDisc address (Token=eui64)
6600 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6601 # NDisc address (temporary)
6602 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')
6604 print('### ip -6 route show type unreachable')
6605 output
= check_output('ip -6 route show type unreachable')
6607 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6609 print('### ip -6 route show dev veth99')
6610 output
= check_output('ip -6 route show dev veth99')
6612 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6614 print('### ip -6 route show dev test1')
6615 output
= check_output('ip -6 route show dev test1')
6617 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6619 print('### ip -6 route show dev dummy98')
6620 output
= check_output('ip -6 route show dev dummy98')
6622 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6624 print('### ip -6 route show dev dummy99')
6625 output
= check_output('ip -6 route show dev dummy99')
6627 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6629 print('### ip -6 route show dev veth97')
6630 output
= check_output('ip -6 route show dev veth97')
6632 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6634 print('### ip -6 route show dev veth97-peer')
6635 output
= check_output('ip -6 route show dev veth97-peer')
6637 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6639 print('### ip -6 route show dev veth98')
6640 output
= check_output('ip -6 route show dev veth98')
6642 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6644 print('### ip -6 route show dev veth98-peer')
6645 output
= check_output('ip -6 route show dev veth98-peer')
6647 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6649 print('### ip -6 address show dev dummy97 scope global')
6650 output
= check_output('ip -6 address show dev dummy97 scope global')
6652 # address in IA_PD (Token=static)
6653 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6654 # address in IA_PD (temporary)
6655 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')
6657 print('### ip -6 route show dev dummy97')
6658 output
= check_output('ip -6 route show dev dummy97')
6660 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6662 print(f
'### ip -d link show dev {tunnel_name}')
6663 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6665 self
.assertIn('link/sit 10.100.100.', output
)
6666 self
.assertIn('local 10.100.100.', output
)
6667 self
.assertIn('ttl 64', output
)
6668 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6669 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6671 print(f
'### ip -6 address show dev {tunnel_name}')
6672 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6674 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')
6675 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6677 print(f
'### ip -6 route show dev {tunnel_name}')
6678 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6680 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6681 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6683 print('### ip -6 route show default')
6684 output
= check_output('ip -6 route show default')
6686 self
.assertIn('default', output
)
6687 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6689 def test_dhcp4_6rd(self
):
6690 def get_dhcp_6rd_prefix(link
):
6691 description
= get_link_description(link
)
6693 self
.assertIn('DHCPv4Client', description
.keys())
6694 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6696 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6697 self
.assertIn('Prefix', prefixInfo
.keys())
6698 self
.assertIn('PrefixLength', prefixInfo
.keys())
6699 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6700 self
.assertIn('BorderRouters', prefixInfo
.keys())
6704 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6705 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6706 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6707 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6708 '25-dhcp-pd-downstream-dummy97.network',
6709 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6710 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6711 '80-6rd-tunnel.network')
6714 self
.wait_online(['veth-peer:routable'])
6717 # 6rd-prefix: 2001:db8::/32
6718 # br-addresss: 10.0.0.1
6720 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',
6721 ipv4_range
='10.100.100.100,10.100.100.200',
6722 ipv4_router
='10.0.0.1')
6723 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6724 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6726 # Check the DBus interface for assigned prefix information
6727 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6729 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6730 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6731 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6732 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6734 # Test case for a downstream which appears later
6735 check_output('ip link add dummy97 type dummy')
6736 self
.wait_online(['dummy97:routable'])
6740 for name
in os
.listdir('/sys/class/net/'):
6741 if name
.startswith('6rd-'):
6745 self
.wait_online([f
'{tunnel_name}:routable'])
6747 self
.verify_dhcp4_6rd(tunnel_name
)
6749 # Test case for reconfigure
6750 networkctl_reconfigure('dummy98', 'dummy99')
6751 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6753 self
.verify_dhcp4_6rd(tunnel_name
)
6755 print('Wait for the DHCP lease to be renewed/rebind')
6758 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6759 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6761 self
.verify_dhcp4_6rd(tunnel_name
)
6763 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
6771 def test_ipv6_route_prefix(self
):
6772 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
6773 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6776 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6778 output
= check_output('ip address show dev veth-peer')
6780 self
.assertIn('inet6 2001:db8:0:1:', output
)
6781 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6782 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6784 output
= check_output('ip -6 route show dev veth-peer')
6786 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6787 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6788 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
6789 self
.assertIn('2001:db0:fff::/64 via ', output
)
6790 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6791 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
6793 output
= check_output('ip address show dev veth99')
6795 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6796 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
6797 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
6798 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6800 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6802 self
.assertRegex(output
, '2001:db8:1:1::2')
6804 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6806 self
.assertIn('example.com', output
)
6808 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6811 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth-peer', env
=env
)
6815 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
6817 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
6818 self
.assertEqual(prefix
, '64:ff9b::')
6820 prefix_length
= pref64
['PrefixLength']
6821 self
.assertEqual(prefix_length
, 96)
6823 def test_ipv6_route_prefix_deny_list(self
):
6824 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
6825 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6828 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6830 output
= check_output('ip address show dev veth-peer')
6832 self
.assertIn('inet6 2001:db8:0:1:', output
)
6833 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6835 output
= check_output('ip -6 route show dev veth-peer')
6837 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6838 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6839 self
.assertIn('2001:db0:fff::/64 via ', output
)
6840 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6842 output
= check_output('ip address show dev veth99')
6844 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6845 self
.assertIn('inet6 2001:db8:0:2:', output
)
6847 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6849 self
.assertRegex(output
, '2001:db8:1:1::2')
6851 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6853 self
.assertIn('example.com', output
)
6855 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
6863 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
6869 self
.wait_online(['dummy98:routable'])
6870 self
.check_link_attr('dummy98', 'mtu', mtu
)
6871 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6873 # test normal restart
6875 self
.wait_online(['dummy98:routable'])
6876 self
.check_link_attr('dummy98', 'mtu', mtu
)
6877 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6880 self
.reset_check_mtu(mtu
, ipv6_mtu
)
6882 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
6883 ''' test setting mtu/ipv6_mtu with interface already up '''
6886 # note - changing the device mtu resets the ipv6 mtu
6887 check_output('ip link set up mtu 1501 dev dummy98')
6888 check_output('ip link set up mtu 1500 dev dummy98')
6889 self
.check_link_attr('dummy98', 'mtu', '1500')
6890 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
6892 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
6894 def test_mtu_network(self
):
6895 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
6896 self
.check_mtu('1600')
6898 def test_mtu_netdev(self
):
6899 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
6900 # note - MTU set by .netdev happens ONLY at device creation!
6901 self
.check_mtu('1600', reset
=False)
6903 def test_mtu_link(self
):
6904 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
6905 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
6906 self
.check_mtu('1600', reset
=False)
6908 def test_ipv6_mtu(self
):
6909 ''' set ipv6 mtu without setting device mtu '''
6910 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
6911 self
.check_mtu('1500', '1400')
6913 def test_ipv6_mtu_toolarge(self
):
6914 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
6915 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6916 self
.check_mtu('1500', '1500')
6918 def test_mtu_network_ipv6_mtu(self
):
6919 ''' set ipv6 mtu and set device mtu via network file '''
6920 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
6921 self
.check_mtu('1600', '1550')
6923 def test_mtu_netdev_ipv6_mtu(self
):
6924 ''' set ipv6 mtu and set device mtu via netdev file '''
6925 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6926 self
.check_mtu('1600', '1550', reset
=False)
6928 def test_mtu_link_ipv6_mtu(self
):
6929 ''' set ipv6 mtu and set device mtu via link file '''
6930 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
6931 self
.check_mtu('1600', '1550', reset
=False)
6934 if __name__
== '__main__':
6935 parser
= argparse
.ArgumentParser()
6936 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
6937 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
6938 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
6939 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
6940 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
6941 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
6942 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
6943 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
6944 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
6945 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
6946 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
6947 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
6948 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
6949 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
6950 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
6951 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
6952 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
)
6953 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
6956 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
6957 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
6958 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
6959 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
6960 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
6961 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
6962 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6963 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
6964 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
6965 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
6966 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
6967 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6968 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
6971 networkd_bin
= ns
.networkd_bin
6973 resolved_bin
= ns
.resolved_bin
6974 if ns
.timesyncd_bin
:
6975 timesyncd_bin
= ns
.timesyncd_bin
6977 udevd_bin
= ns
.udevd_bin
6978 if ns
.wait_online_bin
:
6979 wait_online_bin
= ns
.wait_online_bin
6980 if ns
.networkctl_bin
:
6981 networkctl_bin
= ns
.networkctl_bin
6982 if ns
.resolvectl_bin
:
6983 resolvectl_bin
= ns
.resolvectl_bin
6984 if ns
.timedatectl_bin
:
6985 timedatectl_bin
= ns
.timedatectl_bin
6987 udevadm_bin
= ns
.udevadm_bin
6990 systemd_source_dir
= ns
.source_dir
6992 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
6993 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
6994 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
6996 use_valgrind
= ns
.use_valgrind
6997 enable_debug
= ns
.enable_debug
6998 asan_options
= ns
.asan_options
6999 lsan_options
= ns
.lsan_options
7000 ubsan_options
= ns
.ubsan_options
7001 with_coverage
= ns
.with_coverage
7004 # Do not forget the trailing space.
7005 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7007 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7008 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7009 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7010 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7011 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7014 env
.update({'ASAN_OPTIONS': asan_options
})
7016 env
.update({'LSAN_OPTIONS': lsan_options
})
7018 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7020 env
.update({'SYSTEMD_MEMPOOL': '0'})
7022 wait_online_env
= env
.copy()
7024 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7026 sys
.argv
[1:] = unknown_args
7027 unittest
.main(verbosity
=3)