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]
262 return version
.parse(kver
) >= version
.parse(min_kernel_version
)
265 check_output(*udevadm_cmd
, 'control', '--reload')
267 def copy_network_unit(*units
, copy_dropins
=True):
269 Copy networkd unit files into the testbed.
271 Any networkd unit file type can be specified, as well as drop-in files.
273 By default, all drop-ins for a specified unit file are copied in;
274 to avoid that specify dropins=False.
276 When a drop-in file is specified, its unit file is also copied in automatically.
279 mkdir_p(network_unit_dir
)
281 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
282 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
284 if unit
.endswith('.conf'):
286 unit
= os
.path
.dirname(dropin
).rstrip('.d')
287 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
289 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
291 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
293 if unit
.endswith('.link'):
299 def remove_network_unit(*units
):
301 Remove previously copied unit files from the testbed.
303 Drop-ins will be removed automatically.
307 rm_f(os
.path
.join(network_unit_dir
, unit
))
308 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
310 if unit
.endswith('.link') or unit
.endswith('.link.d'):
316 def clear_network_units():
318 if os
.path
.exists(network_unit_dir
):
319 units
= os
.listdir(network_unit_dir
)
321 if unit
.endswith('.link') or unit
.endswith('.link.d'):
324 rm_rf(network_unit_dir
)
329 def copy_networkd_conf_dropin(*dropins
):
330 """Copy networkd.conf dropin files into the testbed."""
331 mkdir_p(networkd_conf_dropin_dir
)
332 for dropin
in dropins
:
333 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
335 def remove_networkd_conf_dropin(*dropins
):
336 """Remove previously copied networkd.conf dropin files from the testbed."""
337 for dropin
in dropins
:
338 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
340 def clear_networkd_conf_dropins():
341 rm_rf(networkd_conf_dropin_dir
)
343 def setup_systemd_udev_rules():
344 if not systemd_udev_rules_build_dir
:
347 mkdir_p(udev_rules_dir
)
349 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
350 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
352 for rule
in os
.listdir(path
):
353 if not rule
.endswith(".rules"):
355 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
357 def copy_udev_rule(*rules
):
358 """Copy udev rules"""
359 mkdir_p(udev_rules_dir
)
361 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
363 def remove_udev_rule(*rules
):
364 """Remove previously copied udev rules"""
366 rm_f(os
.path
.join(udev_rules_dir
, rule
))
368 def clear_udev_rules():
369 rm_rf(udev_rules_dir
)
371 def save_active_units():
372 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
373 'systemd-resolved.service', 'systemd-timesyncd.service',
374 'firewalld.service']:
375 if call(f
'systemctl is-active --quiet {u}') == 0:
376 call(f
'systemctl stop {u}')
377 active_units
.append(u
)
379 def restore_active_units():
380 if 'systemd-networkd.socket' in active_units
:
381 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
382 for u
in active_units
:
383 call(f
'systemctl restart {u}')
385 def create_unit_dropin(unit
, contents
):
386 mkdir_p(f
'/run/systemd/system/{unit}.d')
387 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
388 f
.write('\n'.join(contents
))
390 def create_service_dropin(service
, command
, additional_settings
=None):
394 f
'ExecStart=!!{valgrind_cmd}{command}',
397 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
399 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
401 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
403 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
404 if asan_options
or lsan_options
or ubsan_options
:
405 drop_in
+= ['SystemCallFilter=']
406 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
407 drop_in
+= ['MemoryDenyWriteExecute=no']
410 'Environment=SYSTEMD_MEMPOOL=0',
418 if additional_settings
:
419 drop_in
+= additional_settings
421 create_unit_dropin(f
'{service}.service', drop_in
)
423 def link_exists(link
):
424 return call_quiet(f
'ip link show {link}') == 0
426 def link_resolve(link
):
427 return check_output(f
'ip link show {link}').split(':')[1].strip()
429 def remove_link(*links
, protect
=False):
431 if protect
and link
in protected_links
:
433 if link_exists(link
):
434 call(f
'ip link del dev {link}')
436 def save_existing_links():
437 links
= os
.listdir('/sys/class/net')
439 if link_exists(link
):
440 protected_links
.add(link
)
442 print('### The following links will be protected:')
443 print(', '.join(sorted(list(protected_links
))))
446 links
= os
.listdir('/sys/class/net')
447 remove_link(*links
, protect
=True)
449 def flush_nexthops():
450 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
451 # Hence, we cannot restore nexthops in a simple way.
452 # Let's assume there is no nexthop used in the system
453 call_quiet('ip nexthop flush')
456 # pylint: disable=global-statement
458 saved_routes
= check_output('ip route show table all')
459 print('### The following routes will be protected:')
464 output
= check_output('ip route show table all')
465 for line
in output
.splitlines():
466 if line
in saved_routes
:
468 if 'proto kernel' in line
:
470 if ' dev ' in line
and not ' dev lo ' in line
:
474 print('### Removing routes that did not exist when the test started.')
476 call(f
'ip route del {line}')
478 def save_routing_policy_rules():
479 # pylint: disable=global-statement
480 global saved_ipv4_rules
, saved_ipv6_rules
482 output
= check_output(f
'ip -{ipv} rule show')
483 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
487 saved_ipv4_rules
= save(4)
488 saved_ipv6_rules
= save(6)
490 def flush_routing_policy_rules():
491 def flush(ipv
, saved_rules
):
493 output
= check_output(f
'ip -{ipv} rule show')
494 for line
in output
.splitlines():
495 if line
in saved_rules
:
499 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
501 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
502 priority
= words
[0].rstrip(':')
503 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
505 flush(4, saved_ipv4_rules
)
506 flush(6, saved_ipv6_rules
)
508 def flush_fou_ports():
509 ret
= run('ip fou show')
510 if ret
.returncode
!= 0:
511 return # fou may not be supported
512 for line
in ret
.stdout
.splitlines():
513 port
= line
.split()[1]
514 call(f
'ip fou del port {port}')
516 def flush_l2tp_tunnels():
518 ret
= run('ip l2tp show tunnel')
519 if ret
.returncode
!= 0:
520 return # l2tp may not be supported
521 for line
in ret
.stdout
.splitlines():
523 if words
[0] == 'Tunnel':
524 tid
= words
[1].rstrip(',')
525 call(f
'ip l2tp del tunnel tunnel_id {tid}')
528 # Removing L2TP tunnel is asynchronous and slightly takes a time.
531 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
532 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
536 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
539 # pylint: disable=global-statement
540 global saved_timezone
541 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
542 if r
.returncode
== 0:
543 saved_timezone
= r
.stdout
.rstrip()
544 print(f
'### Saved timezone: {saved_timezone}')
546 def restore_timezone():
548 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
550 def read_link_attr(*args
):
551 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
552 return f
.readline().strip()
554 def read_manager_state_file():
555 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
558 def read_link_state_file(link
):
559 ifindex
= read_link_attr(link
, 'ifindex')
560 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
561 with
open(path
, encoding
='utf-8') as f
:
564 def read_ip_sysctl_attr(link
, attribute
, ipv
):
565 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
566 return f
.readline().strip()
568 def read_ipv6_sysctl_attr(link
, attribute
):
569 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
571 def read_ipv4_sysctl_attr(link
, attribute
):
572 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
574 def stop_by_pid_file(pid_file
):
575 if not os
.path
.exists(pid_file
):
577 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
578 pid
= f
.read().rstrip(' \t\r\n\0')
579 os
.kill(int(pid
), signal
.SIGTERM
)
583 print(f
"PID {pid} is still alive, waiting...")
586 if e
.errno
== errno
.ESRCH
:
588 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
591 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'):
593 ra_mode
= f
',{ra_mode}'
599 f
'--log-facility={dnsmasq_log_file}',
600 '--log-queries=extra',
602 f
'--pid-file={dnsmasq_pid_file}',
603 '--conf-file=/dev/null',
605 f
'--interface={interface}',
606 f
'--dhcp-leasefile={dnsmasq_lease_file}',
608 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
609 f
'--dhcp-range={ipv4_range},2m',
610 '--dhcp-option=option:mtu,1492',
611 f
'--dhcp-option=option:router,{ipv4_router}',
614 ) + additional_options
615 check_output(*command
)
618 stop_by_pid_file(dnsmasq_pid_file
)
619 rm_f(dnsmasq_lease_file
)
620 rm_f(dnsmasq_log_file
)
622 def read_dnsmasq_log_file():
623 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
626 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
627 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
628 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
629 touch(isc_dhcpd_lease_file
)
630 check_output(isc_dhcpd_command
)
632 def stop_isc_dhcpd():
633 stop_by_pid_file(isc_dhcpd_pid_file
)
634 rm_f(isc_dhcpd_lease_file
)
636 def get_dbus_link_path(link
):
637 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
638 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
639 'GetLinkByName', 's', link
])
641 assert out
.startswith(b
'io ')
643 assert out
.endswith(b
'"')
645 return out
[:-1].split('"')[1]
647 def get_dhcp_client_state(link
, family
):
648 link_path
= get_dbus_link_path(link
)
650 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
651 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
652 assert out
.startswith(b
's "')
654 assert out
.endswith(b
'"')
655 return out
[3:-1].decode()
657 def get_dhcp4_client_state(link
):
658 return get_dhcp_client_state(link
, '4')
660 def get_dhcp6_client_state(link
):
661 return get_dhcp_client_state(link
, '6')
663 def get_link_description(link
):
664 link_path
= get_dbus_link_path(link
)
666 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
667 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
668 assert out
.startswith(b
's "')
670 assert out
.endswith(b
'"')
671 json_raw
= out
[2:].decode()
673 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
674 check_json(description
)
675 return json
.loads(description
) # Now parse the json
677 def start_radvd(*additional_options
, config_file
):
678 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
681 f
'--pidfile={radvd_pid_file}',
682 f
'--config={config_file_path}',
683 '--logmethod=stderr',
684 ) + additional_options
685 check_output(*command
)
688 stop_by_pid_file(radvd_pid_file
)
690 def radvd_check_config(config_file
):
691 if not shutil
.which('radvd'):
692 print('radvd is not installed, assuming the config check failed')
695 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
696 # set up (one instance is @unittest.skipX())
697 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
698 return call(f
'radvd --config={config_file_path} --configtest') == 0
700 def networkd_invocation_id():
701 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
703 def read_networkd_log(invocation_id
=None, since
=None):
704 if not invocation_id
:
705 invocation_id
= networkd_invocation_id()
708 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
711 command
.append(f
'--since={since}')
712 return check_output(*command
)
714 def stop_networkd(show_logs
=True):
716 invocation_id
= networkd_invocation_id()
717 check_output('systemctl stop systemd-networkd.socket')
718 check_output('systemctl stop systemd-networkd.service')
720 print(read_networkd_log(invocation_id
))
721 # Check if networkd exits cleanly.
722 assert call_quiet('systemctl is-failed -q systemd-networkd.service') == 1
724 def start_networkd():
725 check_output('systemctl start systemd-networkd')
727 def restart_networkd(show_logs
=True):
729 invocation_id
= networkd_invocation_id()
730 check_output('systemctl restart systemd-networkd.service')
732 print(read_networkd_log(invocation_id
))
735 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
737 def networkctl_reconfigure(*links
):
738 check_output(*networkctl_cmd
, 'reconfigure', *links
, env
=env
)
740 def networkctl_reload(sleep_time
=1):
741 check_output(*networkctl_cmd
, 'reload', env
=env
)
742 # 'networkctl reload' asynchronously reconfigure links.
743 # Hence, we need to wait for a short time for link to be in configuring state.
745 time
.sleep(sleep_time
)
750 def tear_down_common():
751 # 1. stop DHCP/RA servers
757 call_quiet('rmmod netdevsim')
758 call_quiet('rmmod sch_teql')
760 # 3. remove network namespace
761 call_quiet('ip netns del ns99')
771 clear_network_units()
772 clear_networkd_conf_dropins()
777 flush_routing_policy_rules()
781 rm_rf(networkd_ci_temp_dir
)
782 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
784 clear_network_units()
785 clear_networkd_conf_dropins()
788 setup_systemd_udev_rules()
789 copy_udev_rule('00-debug-net.rules')
793 save_existing_links()
795 save_routing_policy_rules()
798 create_service_dropin('systemd-networkd', networkd_bin
,
801 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
803 'StartLimitIntervalSec=0'])
804 create_service_dropin('systemd-resolved', resolved_bin
)
805 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
807 # TODO: also run udevd with sanitizers, valgrind, or coverage
808 #create_service_dropin('systemd-udevd', udevd_bin,
809 # f'{udevadm_bin} control --reload --timeout 0')
811 'systemd-udevd.service',
815 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
819 'systemd-networkd.socket',
822 'StartLimitIntervalSec=0',
826 check_output('systemctl daemon-reload')
827 print(check_output('systemctl cat systemd-networkd.service'))
828 print(check_output('systemctl cat systemd-resolved.service'))
829 print(check_output('systemctl cat systemd-timesyncd.service'))
830 print(check_output('systemctl cat systemd-udevd.service'))
831 check_output('systemctl restart systemd-resolved.service')
832 check_output('systemctl restart systemd-timesyncd.service')
833 check_output('systemctl restart systemd-udevd.service')
835 def tearDownModule():
836 rm_rf(networkd_ci_temp_dir
)
838 clear_network_units()
839 clear_networkd_conf_dropins()
843 rm_rf('/run/systemd/system/systemd-networkd.service.d')
844 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
845 rm_rf('/run/systemd/system/systemd-resolved.service.d')
846 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
847 rm_rf('/run/systemd/system/systemd-udevd.service.d')
848 check_output('systemctl daemon-reload')
849 check_output('systemctl restart systemd-udevd.service')
850 restore_active_units()
853 # pylint: disable=no-member
855 def check_link_exists(self
, link
, expected
=True):
857 self
.assertTrue(link_exists(link
))
859 self
.assertFalse(link_exists(link
))
861 def check_link_attr(self
, *args
):
862 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
864 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
865 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
866 if allow_enoent
and not os
.path
.exists(path
):
868 with
open(path
, encoding
='utf-8') as f
:
869 self
.assertEqual(f
.readline().strip(), expected
)
871 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
872 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
874 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
875 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
877 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
878 def links_exist(*links
):
880 if not link_exists(link
):
884 for iteration
in range(timeout
+ 1):
888 if links_exist(*links
):
891 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
894 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
895 # wait for the interface is activated.
896 invocation_id
= check_output('systemctl show systemd-networkd -p InvocationID --value')
897 needle
= f
'{link}: Bringing link {state}'
899 for iteration
in range(timeout
+ 1):
902 if not link_exists(link
):
904 output
= check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id
)
905 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
908 self
.fail(f
'Timed out waiting for {link} activated.')
911 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
912 """Wait for the link to reach the specified operstate and/or setup state.
914 Specify None or '' for either operstate or setup_state to ignore that state.
915 This will recheck until the state conditions are met or the timeout expires.
917 If the link successfully matches the requested state, this returns True.
918 If this times out waiting for the link to match, the behavior depends on the
919 'fail_assert' parameter; if True, this causes a test assertion failure,
920 otherwise this returns False. The default is to cause assertion failure.
922 Note that this function matches on *exactly* the given operstate and setup_state.
923 To wait for a link to reach *or exceed* a given operstate, use wait_online().
930 for secs
in range(setup_timeout
+ 1):
933 if not link_exists(link
):
935 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', link
, env
=env
)
936 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
940 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
943 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
944 """Wait for the links to reach the specified operstate and/or setup state.
946 This is similar to wait_operstate() but can be used for multiple links,
947 and it also calls systemd-networkd-wait-online to wait for the given operstate.
948 The operstate should be specified in the link name, like 'eth0:degraded'.
949 If just a link name is provided, wait-online's default operstate to wait for is degraded.
951 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
952 'setup_timeout' controls the per-link timeout waiting for the setup_state.
954 Set 'bool_any' to True to wait for any (instead of all) of the given links.
955 If this is set, no setup_state checks are done.
957 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
958 This is applied only for the operational state 'degraded' or above.
960 Note that this function waits for the links to reach *or exceed* the given operstate.
961 However, the setup_state, if specified, must be matched *exactly*.
963 This returns if the links reached the requested operstate/setup_state; otherwise it
964 raises CalledProcessError or fails test assertion.
966 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
974 check_output(*args
, env
=wait_online_env
)
975 except subprocess
.CalledProcessError
:
976 # show detailed status on failure
977 for link
in links_with_operstate
:
978 name
= link
.split(':')[0]
979 if link_exists(name
):
980 call(*networkctl_cmd
, '-n', '0', 'status', name
, env
=env
)
982 if not bool_any
and setup_state
:
983 for link
in links_with_operstate
:
984 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
986 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
987 for i
in range(timeout_sec
):
990 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
991 if re
.search(address_regex
, output
) and 'tentative' not in output
:
994 self
.assertRegex(output
, address_regex
)
996 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
997 for i
in range(timeout_sec
):
1000 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1001 if not re
.search(address_regex
, output
):
1004 self
.assertNotRegex(output
, address_regex
)
1006 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1007 for i
in range(timeout_sec
):
1010 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1011 if re
.search(route_regex
, output
):
1014 self
.assertRegex(output
, route_regex
)
1016 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1017 if not shutil
.which('selinuxenabled'):
1018 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1019 elif call_quiet('selinuxenabled') != 0:
1020 print('## Checking NetLabel skipped: SELinux disabled.')
1021 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1022 print('## Checking NetLabel skipped: netlabelctl command not found.')
1024 output
= check_output('netlabelctl unlbl list')
1026 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1028 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1029 if not shutil
.which('nft'):
1030 print('## Setting up NFT sets skipped: nft command not found.')
1032 if call(f
'nft add table inet sd_test') != 0:
1033 print('## Setting up NFT table failed.')
1035 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1036 print('## Setting up NFT sets failed.')
1039 def teardown_nftset(self
, *filters
):
1040 if not shutil
.which('nft'):
1041 print('## Tearing down NFT sets skipped: nft command not found.')
1043 for filter_name
in filters
:
1044 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1045 print('## Tearing down NFT sets failed.')
1047 if call(f
'nft delete table inet sd_test') != 0:
1048 print('## Tearing down NFT table failed.')
1051 def check_nftset(self
, filter_name
, contents
):
1052 if not shutil
.which('nft'):
1053 print('## Checking NFT sets skipped: nft command not found.')
1055 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1057 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1059 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1067 @expectedFailureIfAlternativeNameIsNotAvailable()
1068 def test_altname(self
):
1069 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1071 self
.wait_online(['dummy98:degraded'])
1073 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1074 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1076 @expectedFailureIfAlternativeNameIsNotAvailable()
1077 def test_rename_to_altname(self
):
1078 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1079 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1081 self
.wait_online(['dummyalt:degraded'])
1083 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummyalt', env
=env
)
1084 self
.assertIn('hogehogehogehogehogehoge', output
)
1085 self
.assertNotIn('dummy98', output
)
1087 def test_reconfigure(self
):
1088 copy_network_unit('25-address-static.network', '12-dummy.netdev')
1090 self
.wait_online(['dummy98:routable'])
1092 output
= check_output('ip -4 address show dev dummy98')
1094 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1095 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1096 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1098 check_output('ip address del 10.1.2.3/16 dev dummy98')
1099 check_output('ip address del 10.1.2.4/16 dev dummy98')
1100 check_output('ip address del 10.2.2.4/16 dev dummy98')
1102 networkctl_reconfigure('dummy98')
1103 self
.wait_online(['dummy98:routable'])
1105 output
= check_output('ip -4 address show dev dummy98')
1107 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1108 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1109 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1111 remove_network_unit('25-address-static.network')
1114 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1116 output
= check_output('ip -4 address show dev dummy98')
1118 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1119 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1120 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1122 copy_network_unit('25-address-static.network')
1124 self
.wait_online(['dummy98:routable'])
1126 output
= check_output('ip -4 address show dev dummy98')
1128 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1129 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1130 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1132 def test_renew(self
):
1134 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
1135 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
1137 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1138 self
.assertIn('Gateway: 192.168.5.3', output
)
1139 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1140 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1142 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1145 output
= check_output(*networkctl_cmd
, '--lines=0', '--stats', '--all', '--full', '--json=short', 'status')
1148 for verb
in ['renew', 'forcerenew']:
1149 call_check(*networkctl_cmd
, verb
, 'veth99')
1151 call_check(*networkctl_cmd
, verb
, 'veth99', 'veth99', 'veth99')
1154 def test_up_down(self
):
1155 copy_network_unit('25-address-static.network', '12-dummy.netdev')
1157 self
.wait_online(['dummy98:routable'])
1159 call_check(*networkctl_cmd
, 'down', 'dummy98')
1160 self
.wait_online(['dummy98:off'])
1161 call_check(*networkctl_cmd
, 'up', 'dummy98')
1162 self
.wait_online(['dummy98:routable'])
1163 call_check(*networkctl_cmd
, 'down', 'dummy98', 'dummy98', 'dummy98')
1164 self
.wait_online(['dummy98:off'])
1165 call_check(*networkctl_cmd
, 'up', 'dummy98', 'dummy98', 'dummy98')
1166 self
.wait_online(['dummy98:routable'])
1168 def test_reload(self
):
1171 copy_network_unit('11-dummy.netdev')
1173 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1175 copy_network_unit('11-dummy.network')
1177 self
.wait_online(['test1:degraded'])
1179 remove_network_unit('11-dummy.network')
1181 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1183 remove_network_unit('11-dummy.netdev')
1185 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1187 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1189 self
.wait_operstate('test1', 'degraded')
1191 def test_glob(self
):
1192 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1195 self
.wait_online(['test1:degraded'])
1197 output
= check_output(*networkctl_cmd
, 'list', env
=env
)
1198 self
.assertRegex(output
, '1 lo ')
1199 self
.assertRegex(output
, 'test1')
1201 output
= check_output(*networkctl_cmd
, 'list', 'test1', env
=env
)
1202 self
.assertNotRegex(output
, '1 lo ')
1203 self
.assertRegex(output
, 'test1')
1205 output
= check_output(*networkctl_cmd
, 'list', 'te*', env
=env
)
1206 self
.assertNotRegex(output
, '1 lo ')
1207 self
.assertRegex(output
, 'test1')
1209 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'te*', env
=env
)
1210 self
.assertNotRegex(output
, '1: lo ')
1211 self
.assertRegex(output
, 'test1')
1213 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'tes[a-z][0-9]', env
=env
)
1214 self
.assertNotRegex(output
, '1: lo ')
1215 self
.assertRegex(output
, 'test1')
1218 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1221 self
.wait_online(['test1:degraded'])
1223 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1224 self
.assertRegex(output
, 'MTU: 1600')
1226 def test_type(self
):
1227 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1229 self
.wait_online(['test1:degraded'])
1231 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1233 self
.assertRegex(output
, 'Type: ether')
1235 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1237 self
.assertRegex(output
, 'Type: loopback')
1239 def test_udev_link_file(self
):
1240 copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link')
1242 self
.wait_online(['test1:degraded'])
1244 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1246 self
.assertRegex(output
, r
'Link File: /run/systemd/network/25-default.link')
1247 self
.assertRegex(output
, r
'Network File: /run/systemd/network/11-dummy.network')
1249 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1250 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1251 # Let's reprocess the interface and drop the property.
1252 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1253 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1255 self
.assertRegex(output
, r
'Link File: n/a')
1256 self
.assertRegex(output
, r
'Network File: n/a')
1258 def test_delete_links(self
):
1259 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1260 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1263 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
1265 check_output(*networkctl_cmd
, 'delete', 'test1', 'veth99', env
=env
)
1266 self
.check_link_exists('test1', expected
=False)
1267 self
.check_link_exists('veth99', expected
=False)
1268 self
.check_link_exists('veth-peer', expected
=False)
1270 def test_label(self
):
1271 call_check(*networkctl_cmd
, 'label')
1273 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1281 @expectedFailureIfAlternativeNameIsNotAvailable()
1282 def test_match(self
):
1283 copy_network_unit('12-dummy-mac.netdev',
1284 '12-dummy-match-mac-01.network',
1285 '12-dummy-match-mac-02.network',
1286 '12-dummy-match-renamed.network',
1287 '12-dummy-match-altname.network',
1288 '12-dummy-altname.link')
1291 self
.wait_online(['dummy98:routable'])
1292 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1293 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1294 output
= check_output('ip -4 address show dev dummy98')
1295 self
.assertIn('10.0.0.1/16', output
)
1297 check_output('ip link set dev dummy98 down')
1298 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1300 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1301 self
.wait_online(['dummy98:routable'])
1302 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1303 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1305 check_output('ip link set dev dummy98 down')
1306 check_output('ip link set dev dummy98 name dummy98-1')
1308 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1309 self
.wait_online(['dummy98-1:routable'])
1310 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-1', env
=env
)
1311 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1313 check_output('ip link set dev dummy98-1 down')
1314 check_output('ip link set dev dummy98-1 name dummy98-2')
1315 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1317 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1318 self
.wait_online(['dummy98-2:routable'])
1319 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-2', env
=env
)
1320 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1322 def test_match_udev_property(self
):
1323 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1325 self
.wait_online(['dummy98:routable'])
1327 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1329 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1331 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1339 def test_wait_online_any(self
):
1340 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1343 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
1345 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1346 self
.wait_operstate('test1', 'degraded')
1348 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1356 def test_dropin_and_name_conflict(self
):
1357 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1360 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
1362 output
= check_output('ip link show dropin-test')
1364 self
.assertRegex(output
, '00:50:56:c0:00:28')
1366 @expectedFailureIfModuleIsNotAvailable('bareudp')
1367 def test_bareudp(self
):
1368 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1371 self
.wait_online(['bareudp99:degraded'])
1373 output
= check_output('ip -d link show bareudp99')
1375 self
.assertRegex(output
, 'dstport 1000 ')
1376 self
.assertRegex(output
, 'ethertype ip ')
1378 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1379 def test_batadv(self
):
1380 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1383 self
.wait_online(['batadv99:degraded'])
1385 output
= check_output('ip -d link show batadv99')
1387 self
.assertRegex(output
, 'batadv')
1389 def test_bridge(self
):
1390 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1393 self
.wait_online(['bridge99:no-carrier'])
1395 tick
= os
.sysconf('SC_CLK_TCK')
1396 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1397 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1398 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1399 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1400 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1401 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1402 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1403 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1404 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1406 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
1408 self
.assertRegex(output
, 'Priority: 9')
1409 self
.assertRegex(output
, 'STP: yes')
1410 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1412 output
= check_output('ip -d link show bridge99')
1414 self
.assertIn('vlan_filtering 1 ', output
)
1415 self
.assertIn('vlan_protocol 802.1ad ', output
)
1416 self
.assertIn('vlan_default_pvid 9 ', output
)
1418 def test_bond(self
):
1419 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1422 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1424 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1425 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1426 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1427 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1428 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1429 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1430 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1431 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1432 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1433 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1434 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1436 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1437 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1439 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond99', env
=env
)
1441 self
.assertIn('Mode: 802.3ad', output
)
1442 self
.assertIn('Miimon: 1s', output
)
1443 self
.assertIn('Updelay: 2s', output
)
1444 self
.assertIn('Downdelay: 2s', output
)
1446 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond98', env
=env
)
1448 self
.assertIn('Mode: balance-tlb', output
)
1450 def test_vlan(self
):
1451 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1452 '21-vlan.network', '21-vlan-test1.network')
1455 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1457 output
= check_output('ip -d link show test1')
1459 self
.assertRegex(output
, ' mtu 2000 ')
1461 output
= check_output('ip -d link show vlan99')
1463 self
.assertIn(' mtu 2000 ', output
)
1464 self
.assertIn('REORDER_HDR', output
)
1465 self
.assertIn('LOOSE_BINDING', output
)
1466 self
.assertIn('GVRP', output
)
1467 self
.assertIn('MVRP', output
)
1468 self
.assertIn(' id 99 ', output
)
1469 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1470 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1472 output
= check_output('ip -4 address show dev test1')
1474 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1475 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1477 output
= check_output('ip -4 address show dev vlan99')
1479 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1481 def test_vlan_on_bond(self
):
1482 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1483 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1485 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1486 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1488 self
.wait_online(['bond99:off'])
1489 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1491 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1492 # that the issue is fixed by the commit, let's allow to match both string.
1493 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1497 if log_re
.search(read_networkd_log()):
1502 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1504 self
.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
1506 def test_macvtap(self
):
1508 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1514 print(f
'### test_macvtap(mode={mode})')
1515 with self
.subTest(mode
=mode
):
1516 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1517 '11-dummy.netdev', '25-macvtap.network')
1518 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1519 f
.write('[MACVTAP]\nMode=' + mode
)
1522 self
.wait_online(['macvtap99:degraded',
1523 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1525 output
= check_output('ip -d link show macvtap99')
1527 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1529 def test_macvlan(self
):
1531 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1537 print(f
'### test_macvlan(mode={mode})')
1538 with self
.subTest(mode
=mode
):
1539 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1540 '11-dummy.netdev', '25-macvlan.network')
1541 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1542 f
.write('[MACVLAN]\nMode=' + mode
)
1545 self
.wait_online(['macvlan99:degraded',
1546 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1548 output
= check_output('ip -d link show test1')
1550 self
.assertRegex(output
, ' mtu 2000 ')
1552 output
= check_output('ip -d link show macvlan99')
1554 self
.assertRegex(output
, ' mtu 2000 ')
1555 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1557 remove_link('test1')
1560 check_output("ip link add test1 type dummy")
1561 self
.wait_online(['macvlan99:degraded',
1562 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1564 output
= check_output('ip -d link show test1')
1566 self
.assertRegex(output
, ' mtu 2000 ')
1568 output
= check_output('ip -d link show macvlan99')
1570 self
.assertRegex(output
, ' mtu 2000 ')
1571 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1573 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1574 def test_ipvlan(self
):
1576 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1582 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1583 with self
.subTest(mode
=mode
, flag
=flag
):
1584 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1585 '11-dummy.netdev', '25-ipvlan.network')
1586 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1587 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1590 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1592 output
= check_output('ip -d link show ipvlan99')
1594 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1596 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1597 def test_ipvtap(self
):
1599 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1605 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1606 with self
.subTest(mode
=mode
, flag
=flag
):
1607 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1608 '11-dummy.netdev', '25-ipvtap.network')
1609 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1610 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1613 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1615 output
= check_output('ip -d link show ipvtap99')
1617 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1619 def test_veth(self
):
1620 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1621 '25-veth-mtu.netdev')
1624 self
.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1626 output
= check_output('ip -d link show veth99')
1628 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1629 output
= check_output('ip -d link show veth-peer')
1631 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1633 output
= check_output('ip -d link show veth-mtu')
1635 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1636 self
.assertRegex(output
, 'mtu 1800')
1637 output
= check_output('ip -d link show veth-mtu-peer')
1639 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1640 self
.assertRegex(output
, 'mtu 1800')
1642 def test_tuntap(self
):
1643 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1646 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'])
1648 pid
= networkd_pid()
1649 name
= psutil
.Process(pid
).name()[:15]
1651 output
= check_output('ip -d tuntap show')
1653 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1654 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1656 output
= check_output('ip -d link show testtun99')
1658 # Old ip command does not support IFF_ flags
1659 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1660 self
.assertIn('UP,LOWER_UP', output
)
1662 output
= check_output('ip -d link show testtap99')
1664 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1665 self
.assertIn('UP,LOWER_UP', output
)
1667 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1670 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state
='unmanaged')
1672 pid
= networkd_pid()
1673 name
= psutil
.Process(pid
).name()[:15]
1675 output
= check_output('ip -d tuntap show')
1677 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1678 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1680 output
= check_output('ip -d link show testtun99')
1682 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1683 self
.assertIn('UP,LOWER_UP', output
)
1685 output
= check_output('ip -d link show testtap99')
1687 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1688 self
.assertIn('UP,LOWER_UP', output
)
1690 clear_network_units()
1692 self
.wait_online(['testtun99:off', 'testtap99:off'], setup_state
='unmanaged')
1694 output
= check_output('ip -d tuntap show')
1696 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1697 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1702 output
= check_output('ip -d link show testtun99')
1704 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1705 if 'NO-CARRIER' in output
:
1713 output
= check_output('ip -d link show testtap99')
1715 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1716 if 'NO-CARRIER' in output
:
1721 @expectedFailureIfModuleIsNotAvailable('vrf')
1723 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1726 self
.wait_online(['vrf99:carrier'])
1728 @expectedFailureIfModuleIsNotAvailable('vcan')
1729 def test_vcan(self
):
1730 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1731 '25-vcan98.netdev', '25-vcan98.network')
1734 self
.wait_online(['vcan99:carrier', 'vcan98:carrier'])
1736 # https://github.com/systemd/systemd/issues/30140
1737 output
= check_output('ip -d link show vcan99')
1739 self
.assertIn('mtu 16 ', output
)
1741 output
= check_output('ip -d link show vcan98')
1743 self
.assertIn('mtu 16 ', output
)
1745 @expectedFailureIfModuleIsNotAvailable('vxcan')
1746 def test_vxcan(self
):
1747 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1750 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1752 @expectedFailureIfModuleIsNotAvailable('wireguard')
1753 def test_wireguard(self
):
1754 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1755 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1756 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1757 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1759 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1761 output
= check_output('ip -4 address show dev wg99')
1763 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1765 output
= check_output('ip -4 address show dev wg99')
1767 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1769 output
= check_output('ip -6 address show dev wg99')
1771 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1773 output
= check_output('ip -4 address show dev wg98')
1775 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1777 output
= check_output('ip -6 address show dev wg98')
1779 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1781 output
= check_output('ip -4 route show dev wg99 table 1234')
1783 self
.assertIn('192.168.26.0/24 proto static metric 123', output
)
1785 output
= check_output('ip -6 route show dev wg99 table 1234')
1787 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1789 output
= check_output('ip -6 route show dev wg98 table 1234')
1791 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1792 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1793 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1794 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1795 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1796 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1797 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1798 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1799 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1800 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1801 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1802 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1803 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1804 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1805 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1806 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1807 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1808 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1809 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1810 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1811 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1812 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1813 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1814 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1815 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1816 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1817 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1818 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1819 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1820 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1821 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1822 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1823 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1824 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1825 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1826 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1827 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1828 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1829 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1830 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1831 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1832 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1833 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1834 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1835 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1836 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1838 if shutil
.which('wg'):
1841 output
= check_output('wg show wg99 listen-port')
1842 self
.assertEqual(output
, '51820')
1843 output
= check_output('wg show wg99 fwmark')
1844 self
.assertEqual(output
, '0x4d2')
1845 output
= check_output('wg show wg99 private-key')
1846 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1847 output
= check_output('wg show wg99 allowed-ips')
1848 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1849 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1850 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1851 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1852 output
= check_output('wg show wg99 persistent-keepalive')
1853 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1854 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1855 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1856 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1857 output
= check_output('wg show wg99 endpoints')
1858 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1859 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1860 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1861 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1862 output
= check_output('wg show wg99 preshared-keys')
1863 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1864 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1865 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1866 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1868 output
= check_output('wg show wg98 private-key')
1869 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1871 output
= check_output('wg show wg97 listen-port')
1872 self
.assertEqual(output
, '51821')
1873 output
= check_output('wg show wg97 fwmark')
1874 self
.assertEqual(output
, '0x4d3')
1876 def test_geneve(self
):
1877 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1880 self
.wait_online(['geneve99:degraded'])
1882 output
= check_output('ip -d link show geneve99')
1884 self
.assertRegex(output
, '192.168.22.1')
1885 self
.assertRegex(output
, '6082')
1886 self
.assertRegex(output
, 'udpcsum')
1887 self
.assertRegex(output
, 'udp6zerocsumrx')
1889 def test_ipip_tunnel(self
):
1890 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1891 '25-ipip-tunnel.netdev', '25-tunnel.network',
1892 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1893 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1894 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1896 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1898 output
= check_output('ip -d link show ipiptun99')
1900 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1901 output
= check_output('ip -d link show ipiptun98')
1903 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1904 output
= check_output('ip -d link show ipiptun97')
1906 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1907 output
= check_output('ip -d link show ipiptun96')
1909 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1911 def test_gre_tunnel(self
):
1912 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1913 '25-gre-tunnel.netdev', '25-tunnel.network',
1914 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1915 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1916 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1918 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1920 output
= check_output('ip -d link show gretun99')
1922 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1923 self
.assertRegex(output
, 'ikey 1.2.3.103')
1924 self
.assertRegex(output
, 'okey 1.2.4.103')
1925 self
.assertRegex(output
, 'iseq')
1926 self
.assertRegex(output
, 'oseq')
1927 output
= check_output('ip -d link show gretun98')
1929 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1930 self
.assertRegex(output
, 'ikey 0.0.0.104')
1931 self
.assertRegex(output
, 'okey 0.0.0.104')
1932 self
.assertNotRegex(output
, 'iseq')
1933 self
.assertNotRegex(output
, 'oseq')
1934 output
= check_output('ip -d link show gretun97')
1936 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1937 self
.assertRegex(output
, 'ikey 0.0.0.105')
1938 self
.assertRegex(output
, 'okey 0.0.0.105')
1939 self
.assertNotRegex(output
, 'iseq')
1940 self
.assertNotRegex(output
, 'oseq')
1941 output
= check_output('ip -d link show gretun96')
1943 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1944 self
.assertRegex(output
, 'ikey 0.0.0.106')
1945 self
.assertRegex(output
, 'okey 0.0.0.106')
1946 self
.assertNotRegex(output
, 'iseq')
1947 self
.assertNotRegex(output
, 'oseq')
1949 def test_ip6gre_tunnel(self
):
1950 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
1951 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1952 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1953 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1954 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1957 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1959 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
1961 output
= check_output('ip -d link show ip6gretun99')
1963 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1964 output
= check_output('ip -d link show ip6gretun98')
1966 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1967 output
= check_output('ip -d link show ip6gretun97')
1969 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1970 output
= check_output('ip -d link show ip6gretun96')
1972 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
1974 def test_gretap_tunnel(self
):
1975 copy_network_unit('12-dummy.netdev', '25-gretap.network',
1976 '25-gretap-tunnel.netdev', '25-tunnel.network',
1977 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1979 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1981 output
= check_output('ip -d link show gretap99')
1983 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1984 self
.assertRegex(output
, 'ikey 0.0.0.106')
1985 self
.assertRegex(output
, 'okey 0.0.0.106')
1986 self
.assertRegex(output
, 'iseq')
1987 self
.assertRegex(output
, 'oseq')
1988 self
.assertIn('nopmtudisc', output
)
1989 self
.assertIn('ignore-df', output
)
1990 output
= check_output('ip -d link show gretap98')
1992 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
1993 self
.assertRegex(output
, 'ikey 0.0.0.107')
1994 self
.assertRegex(output
, 'okey 0.0.0.107')
1995 self
.assertRegex(output
, 'iseq')
1996 self
.assertRegex(output
, 'oseq')
1998 def test_ip6gretap_tunnel(self
):
1999 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2000 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2001 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2003 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
2005 output
= check_output('ip -d link show ip6gretap99')
2007 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2008 output
= check_output('ip -d link show ip6gretap98')
2010 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2012 def test_vti_tunnel(self
):
2013 copy_network_unit('12-dummy.netdev', '25-vti.network',
2014 '25-vti-tunnel.netdev', '25-tunnel.network',
2015 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2016 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2017 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2019 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2021 output
= check_output('ip -d link show vtitun99')
2023 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2024 output
= check_output('ip -d link show vtitun98')
2026 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2027 output
= check_output('ip -d link show vtitun97')
2029 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2030 output
= check_output('ip -d link show vtitun96')
2032 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2034 def test_vti6_tunnel(self
):
2035 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2036 '25-vti6-tunnel.netdev', '25-tunnel.network',
2037 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2038 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2040 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2042 output
= check_output('ip -d link show vti6tun99')
2044 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2045 output
= check_output('ip -d link show vti6tun98')
2047 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2048 output
= check_output('ip -d link show vti6tun97')
2050 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2052 def test_ip6tnl_tunnel(self
):
2053 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2054 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2055 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2056 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2057 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2058 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2059 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2061 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2062 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2063 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2065 output
= check_output('ip -d link show ip6tnl99')
2067 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2068 output
= check_output('ip -d link show ip6tnl98')
2070 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2071 output
= check_output('ip -d link show ip6tnl97')
2073 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2074 output
= check_output('ip -d link show ip6tnl-external')
2076 self
.assertIn('ip6tnl-external@NONE:', output
)
2077 self
.assertIn('ip6tnl external ', output
)
2078 output
= check_output('ip -d link show ip6tnl-slaac')
2080 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2082 output
= check_output('ip -6 address show veth99')
2084 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2086 output
= check_output('ip -4 route show default')
2088 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2090 def test_sit_tunnel(self
):
2091 copy_network_unit('12-dummy.netdev', '25-sit.network',
2092 '25-sit-tunnel.netdev', '25-tunnel.network',
2093 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2094 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2095 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2097 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2099 output
= check_output('ip -d link show sittun99')
2101 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2102 output
= check_output('ip -d link show sittun98')
2104 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2105 output
= check_output('ip -d link show sittun97')
2107 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2108 output
= check_output('ip -d link show sittun96')
2110 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2112 def test_isatap_tunnel(self
):
2113 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2114 '25-isatap-tunnel.netdev', '25-tunnel.network')
2116 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2118 output
= check_output('ip -d link show isataptun99')
2120 self
.assertRegex(output
, "isatap ")
2122 def test_6rd_tunnel(self
):
2123 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2124 '25-6rd-tunnel.netdev', '25-tunnel.network')
2126 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2128 output
= check_output('ip -d link show sittun99')
2130 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2132 @expectedFailureIfERSPANv0IsNotSupported()
2133 def test_erspan_tunnel_v0(self
):
2134 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2135 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2136 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2138 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2140 output
= check_output('ip -d link show erspan99')
2142 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2143 self
.assertIn('erspan_ver 0', output
)
2144 self
.assertNotIn('erspan_index 123', output
)
2145 self
.assertNotIn('erspan_dir ingress', output
)
2146 self
.assertNotIn('erspan_hwid 1f', output
)
2147 self
.assertIn('ikey 0.0.0.101', output
)
2148 self
.assertIn('iseq', output
)
2149 self
.assertIn('nopmtudisc', output
)
2150 self
.assertIn('ignore-df', output
)
2151 output
= check_output('ip -d link show erspan98')
2153 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2154 self
.assertIn('erspan_ver 0', output
)
2155 self
.assertNotIn('erspan_index 124', output
)
2156 self
.assertNotIn('erspan_dir egress', output
)
2157 self
.assertNotIn('erspan_hwid 2f', output
)
2158 self
.assertIn('ikey 0.0.0.102', output
)
2159 self
.assertIn('iseq', output
)
2161 def test_erspan_tunnel_v1(self
):
2162 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2163 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2164 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2166 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2168 output
= check_output('ip -d link show erspan99')
2170 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2171 self
.assertIn('erspan_ver 1', output
)
2172 self
.assertIn('erspan_index 123', output
)
2173 self
.assertNotIn('erspan_dir ingress', output
)
2174 self
.assertNotIn('erspan_hwid 1f', output
)
2175 self
.assertIn('ikey 0.0.0.101', output
)
2176 self
.assertIn('okey 0.0.0.101', output
)
2177 self
.assertIn('iseq', output
)
2178 self
.assertIn('oseq', output
)
2179 output
= check_output('ip -d link show erspan98')
2181 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2182 self
.assertIn('erspan_ver 1', output
)
2183 self
.assertIn('erspan_index 124', output
)
2184 self
.assertNotIn('erspan_dir egress', output
)
2185 self
.assertNotIn('erspan_hwid 2f', output
)
2186 self
.assertIn('ikey 0.0.0.102', output
)
2187 self
.assertIn('okey 0.0.0.102', output
)
2188 self
.assertIn('iseq', output
)
2189 self
.assertIn('oseq', output
)
2191 @expectedFailureIfERSPANv2IsNotSupported()
2192 def test_erspan_tunnel_v2(self
):
2193 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2194 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2195 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2197 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2199 output
= check_output('ip -d link show erspan99')
2201 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2202 self
.assertIn('erspan_ver 2', output
)
2203 self
.assertNotIn('erspan_index 123', output
)
2204 self
.assertIn('erspan_dir ingress', output
)
2205 self
.assertIn('erspan_hwid 0x1f', output
)
2206 self
.assertIn('ikey 0.0.0.101', output
)
2207 self
.assertIn('okey 0.0.0.101', output
)
2208 self
.assertIn('iseq', output
)
2209 self
.assertIn('oseq', output
)
2210 output
= check_output('ip -d link show erspan98')
2212 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2213 self
.assertIn('erspan_ver 2', output
)
2214 self
.assertNotIn('erspan_index 124', output
)
2215 self
.assertIn('erspan_dir egress', output
)
2216 self
.assertIn('erspan_hwid 0x2f', output
)
2217 self
.assertIn('ikey 0.0.0.102', output
)
2218 self
.assertIn('okey 0.0.0.102', output
)
2219 self
.assertIn('iseq', output
)
2220 self
.assertIn('oseq', output
)
2222 def test_tunnel_independent(self
):
2223 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2226 self
.wait_online(['ipiptun99:carrier'])
2228 def test_tunnel_independent_loopback(self
):
2229 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2232 self
.wait_online(['ipiptun99:carrier'])
2234 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2235 def test_xfrm(self
):
2236 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2237 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2238 '26-netdev-link-local-addressing-yes.network')
2241 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2243 output
= check_output('ip -d link show dev xfrm98')
2245 self
.assertIn('xfrm98@dummy98:', output
)
2246 self
.assertIn('xfrm if_id 0x98 ', output
)
2248 output
= check_output('ip -d link show dev xfrm99')
2250 self
.assertIn('xfrm99@lo:', output
)
2251 self
.assertIn('xfrm if_id 0x99 ', output
)
2253 @expectedFailureIfModuleIsNotAvailable('fou')
2255 # The following redundant check is necessary for CentOS CI.
2256 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2257 self
.assertTrue(is_module_available('fou'))
2259 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2260 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2261 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2264 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2266 output
= check_output('ip fou show')
2268 self
.assertRegex(output
, 'port 55555 ipproto 4')
2269 self
.assertRegex(output
, 'port 55556 ipproto 47')
2271 output
= check_output('ip -d link show ipiptun96')
2273 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2274 output
= check_output('ip -d link show sittun96')
2276 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2277 output
= check_output('ip -d link show gretun96')
2279 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2280 output
= check_output('ip -d link show gretap96')
2282 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2284 def test_vxlan(self
):
2285 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2286 '25-vxlan.netdev', '25-vxlan.network',
2287 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2288 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2289 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2290 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2293 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2294 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2296 output
= check_output('ip -d -d link show vxlan99')
2298 self
.assertIn('999', output
)
2299 self
.assertIn('5555', output
)
2300 self
.assertIn('l2miss', output
)
2301 self
.assertIn('l3miss', output
)
2302 self
.assertIn('gbp', output
)
2303 # Since [0] some of the options use slightly different names and some
2304 # options with default values are shown only if the -d(etails) setting
2306 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2307 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2308 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2309 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2310 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2311 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2313 output
= check_output('bridge fdb show dev vxlan99')
2315 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2316 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2317 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2319 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'vxlan99', env
=env
)
2321 self
.assertIn('VNI: 999', output
)
2322 self
.assertIn('Destination Port: 5555', output
)
2323 self
.assertIn('Underlying Device: test1', output
)
2325 output
= check_output('bridge fdb show dev vxlan97')
2327 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2328 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2329 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2331 output
= check_output('ip -d link show vxlan-slaac')
2333 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2335 output
= check_output('ip -6 address show veth99')
2337 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2339 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2340 def test_macsec(self
):
2341 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2342 '26-macsec.network', '12-dummy.netdev')
2345 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2347 output
= check_output('ip -d link show macsec99')
2349 self
.assertRegex(output
, 'macsec99@dummy98')
2350 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2351 self
.assertRegex(output
, 'encrypt on')
2353 output
= check_output('ip macsec show macsec99')
2355 self
.assertRegex(output
, 'encrypt on')
2356 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2357 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2358 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2359 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2360 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2361 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2362 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2363 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2364 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2365 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2366 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2368 def test_nlmon(self
):
2369 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2372 self
.wait_online(['nlmon99:carrier'])
2374 @expectedFailureIfModuleIsNotAvailable('ifb')
2376 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2379 self
.wait_online(['ifb99:degraded'])
2381 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2389 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2390 def test_l2tp_udp(self
):
2391 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2392 '25-l2tp-udp.netdev', '25-l2tp.network')
2395 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2397 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2399 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2400 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2401 self
.assertRegex(output
, "Peer tunnel 11")
2402 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2403 self
.assertRegex(output
, "UDP checksum: enabled")
2405 output
= check_output('ip l2tp show session tid 10 session_id 15')
2407 self
.assertRegex(output
, "Session 15 in tunnel 10")
2408 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2409 self
.assertRegex(output
, "interface name: l2tp-ses1")
2411 output
= check_output('ip l2tp show session tid 10 session_id 17')
2413 self
.assertRegex(output
, "Session 17 in tunnel 10")
2414 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2415 self
.assertRegex(output
, "interface name: l2tp-ses2")
2417 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2418 def test_l2tp_ip(self
):
2419 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2420 '25-l2tp-ip.netdev', '25-l2tp.network')
2423 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2425 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2427 self
.assertRegex(output
, "Tunnel 10, encap IP")
2428 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2429 self
.assertRegex(output
, "Peer tunnel 12")
2431 output
= check_output('ip l2tp show session tid 10 session_id 25')
2433 self
.assertRegex(output
, "Session 25 in tunnel 10")
2434 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2435 self
.assertRegex(output
, "interface name: l2tp-ses3")
2437 output
= check_output('ip l2tp show session tid 10 session_id 27')
2439 self
.assertRegex(output
, "Session 27 in tunnel 10")
2440 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2441 self
.assertRegex(output
, "interface name: l2tp-ses4")
2443 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2451 def verify_address_static(
2481 output
= check_output('ip address show dev dummy98')
2485 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2486 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2487 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2488 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2489 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2490 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2493 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2494 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2495 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2498 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2499 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2500 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2503 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2504 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2505 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2506 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2507 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2508 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2511 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2512 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2515 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2516 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2517 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2518 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2521 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2522 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2524 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2526 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2528 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2530 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2533 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2534 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2535 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2536 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2539 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2540 prefix16
= ip4_null_16
[:-len('.0.1')]
2541 self
.assertTrue(ip4_null_24
.endswith('.1'))
2542 prefix24
= ip4_null_24
[:-len('.1')]
2543 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2544 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2545 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2546 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2549 self
.assertNotIn('10.4.4.1', output
)
2550 self
.assertNotIn('10.5.4.1', output
)
2551 self
.assertNotIn('10.5.5.1', output
)
2552 self
.assertNotIn('10.8.2.1', output
)
2553 self
.assertNotIn('10.9.3.1', output
)
2554 self
.assertNotIn('2001:db8:0:f101::2', output
)
2555 self
.assertNotIn('2001:db8:0:f103::4', output
)
2558 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2560 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2563 def test_address_static(self
):
2564 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2566 self
.setup_nftset('addr4', 'ipv4_addr')
2567 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2568 self
.setup_nftset('ifindex', 'iface_index')
2570 self
.wait_online(['dummy98:routable'])
2574 output
= check_output('ip -4 --json address show dev dummy98')
2575 for i
in json
.loads(output
)[0]['addr_info']:
2576 if i
['label'] == 'subnet16':
2577 ip4_null_16
= i
['local']
2578 elif i
['label'] == 'subnet24':
2579 ip4_null_24
= i
['local']
2580 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2581 self
.assertTrue(ip4_null_24
.endswith('.1'))
2585 output
= check_output('ip -6 --json address show dev dummy98')
2586 for i
in json
.loads(output
)[0]['addr_info']:
2587 if i
['prefixlen'] == 73:
2588 ip6_null_73
= i
['local']
2589 elif i
['prefixlen'] == 74:
2590 ip6_null_74
= i
['local']
2591 self
.assertTrue(ip6_null_73
.endswith(':1'))
2592 self
.assertTrue(ip6_null_74
.endswith(':1'))
2594 self
.verify_address_static(
2599 broadcast2
=' brd 10.4.2.255',
2600 broadcast3
=' brd 10.4.3.63',
2601 peer1
=' peer 10.5.1.101/24',
2602 peer2
=' peer 10.5.2.101/24',
2603 peer3
='/24 brd 10.5.3.255',
2604 peer4
=' peer 2001:db8:0:f103::101/128',
2605 peer5
=' peer 2001:db8:0:f103::102/128',
2610 deprecated2
=' deprecated',
2612 deprecated4
=' deprecated',
2614 flag1
=' noprefixroute',
2616 flag3
=' noprefixroute',
2617 flag4
=' home mngtmpaddr',
2618 ip4_null_16
=ip4_null_16
,
2619 ip4_null_24
=ip4_null_24
,
2620 ip6_null_73
=ip6_null_73
,
2621 ip6_null_74
=ip6_null_74
,
2624 # FIXME: re-enable once https://github.com/systemd/systemd/issues/30427 is resolved
2625 #self.check_nftset('addr4', r'10\.10\.1\.1')
2626 #self.check_nftset('network4', r'10\.10\.1\.0/24')
2627 #self.check_nftset('ifindex', 'dummy98')
2629 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2631 copy_network_unit('25-address-static.network.d/10-override.conf')
2633 self
.wait_online(['dummy98:routable'])
2634 self
.verify_address_static(
2635 label1
='new-label1',
2637 label3
='new-label3',
2638 broadcast1
=' brd 10.4.1.255',
2640 broadcast3
=' brd 10.4.3.31',
2641 peer1
=' peer 10.5.1.102/24',
2642 peer2
='/24 brd 10.5.2.255',
2643 peer3
=' peer 10.5.3.102/24',
2644 peer4
=' peer 2001:db8:0:f103::201/128',
2646 peer6
=' peer 2001:db8:0:f103::203/128',
2649 deprecated1
=' deprecated',
2651 deprecated3
=' deprecated',
2655 flag2
=' noprefixroute',
2656 flag3
=' home mngtmpaddr',
2657 flag4
=' noprefixroute',
2658 ip4_null_16
=ip4_null_16
,
2659 ip4_null_24
=ip4_null_24
,
2660 ip6_null_73
=ip6_null_73
,
2661 ip6_null_74
=ip6_null_74
,
2664 networkctl_reconfigure('dummy98')
2665 self
.wait_online(['dummy98:routable'])
2666 self
.verify_address_static(
2667 label1
='new-label1',
2669 label3
='new-label3',
2670 broadcast1
=' brd 10.4.1.255',
2672 broadcast3
=' brd 10.4.3.31',
2673 peer1
=' peer 10.5.1.102/24',
2674 peer2
='/24 brd 10.5.2.255',
2675 peer3
=' peer 10.5.3.102/24',
2676 peer4
=' peer 2001:db8:0:f103::201/128',
2678 peer6
=' peer 2001:db8:0:f103::203/128',
2681 deprecated1
=' deprecated',
2683 deprecated3
=' deprecated',
2687 flag2
=' noprefixroute',
2688 flag3
=' home mngtmpaddr',
2689 flag4
=' noprefixroute',
2690 ip4_null_16
=ip4_null_16
,
2691 ip4_null_24
=ip4_null_24
,
2692 ip6_null_73
=ip6_null_73
,
2693 ip6_null_74
=ip6_null_74
,
2697 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2698 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2699 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2700 output
= check_output('ip address show dev dummy98')
2702 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2703 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2705 # 2. reconfigure the interface, and check the deprecated flag is set again
2706 networkctl_reconfigure('dummy98')
2707 self
.wait_online(['dummy98:routable'])
2708 self
.verify_address_static(
2709 label1
='new-label1',
2711 label3
='new-label3',
2712 broadcast1
=' brd 10.4.1.255',
2714 broadcast3
=' brd 10.4.3.31',
2715 peer1
=' peer 10.5.1.102/24',
2716 peer2
='/24 brd 10.5.2.255',
2717 peer3
=' peer 10.5.3.102/24',
2718 peer4
=' peer 2001:db8:0:f103::201/128',
2720 peer6
=' peer 2001:db8:0:f103::203/128',
2723 deprecated1
=' deprecated',
2725 deprecated3
=' deprecated',
2729 flag2
=' noprefixroute',
2730 flag3
=' home mngtmpaddr',
2731 flag4
=' noprefixroute',
2732 ip4_null_16
=ip4_null_16
,
2733 ip4_null_24
=ip4_null_24
,
2734 ip6_null_73
=ip6_null_73
,
2735 ip6_null_74
=ip6_null_74
,
2738 # test for ENOBUFS issue #17012 (with reload)
2739 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2741 self
.wait_online(['dummy98:routable'])
2742 output
= check_output('ip -4 address show dev dummy98')
2743 for i
in range(1, 254):
2744 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2746 # (with reconfigure)
2747 networkctl_reconfigure('dummy98')
2748 self
.wait_online(['dummy98:routable'])
2749 output
= check_output('ip -4 address show dev dummy98')
2750 for i
in range(1, 254):
2751 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2753 def test_address_ipv4acd(self
):
2754 check_output('ip netns add ns99')
2755 check_output('ip link add veth99 type veth peer veth-peer')
2756 check_output('ip link set veth-peer netns ns99')
2757 check_output('ip link set veth99 up')
2758 check_output('ip netns exec ns99 ip link set veth-peer up')
2759 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2761 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2763 self
.wait_online(['veth99:routable'])
2765 output
= check_output('ip -4 address show dev veth99')
2767 self
.assertNotIn('192.168.100.10/24', output
)
2768 self
.assertIn('192.168.100.11/24', output
)
2770 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2772 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2774 output
= check_output('ip -4 address show dev veth99')
2776 self
.assertNotIn('192.168.100.10/24', output
)
2777 self
.assertIn('192.168.100.11/24', output
)
2779 def test_address_peer_ipv4(self
):
2780 # test for issue #17304
2781 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2783 for trial
in range(2):
2789 self
.wait_online(['dummy98:routable'])
2791 output
= check_output('ip -4 address show dev dummy98')
2792 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2794 @expectedFailureIfModuleIsNotAvailable('vrf')
2795 def test_prefix_route(self
):
2796 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2797 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2798 '25-vrf.netdev', '25-vrf.network')
2799 for trial
in range(2):
2805 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2807 output
= check_output('ip route show table 42 dev dummy98')
2808 print('### ip route show table 42 dev dummy98')
2810 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2811 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2812 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2813 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2814 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2815 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2816 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2817 output
= check_output('ip -6 route show table 42 dev dummy98')
2818 print('### ip -6 route show table 42 dev dummy98')
2822 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2823 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2824 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2825 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2826 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2827 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2828 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2829 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2833 output
= check_output('ip route show dev test1')
2834 print('### ip route show dev test1')
2836 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2837 output
= check_output('ip route show table local dev test1')
2838 print('### ip route show table local dev test1')
2840 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2841 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2842 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2843 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2844 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2845 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2846 output
= check_output('ip -6 route show dev test1')
2847 print('### ip -6 route show dev test1')
2849 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2850 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2851 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2852 output
= check_output('ip -6 route show table local dev test1')
2853 print('### ip -6 route show table local dev test1')
2855 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2856 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2857 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2858 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2859 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2861 def test_configure_without_carrier(self
):
2862 copy_network_unit('11-dummy.netdev')
2864 self
.wait_operstate('test1', 'off', '')
2865 check_output('ip link set dev test1 up carrier off')
2867 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2869 self
.wait_online(['test1:no-carrier'])
2871 carrier_map
= {'on': '1', 'off': '0'}
2872 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2873 for carrier
in ['off', 'on', 'off']:
2874 with self
.subTest(carrier
=carrier
):
2875 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2876 check_output(f
'ip link set dev test1 carrier {carrier}')
2877 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2879 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2881 self
.assertRegex(output
, '192.168.0.15')
2882 self
.assertRegex(output
, '192.168.0.1')
2883 self
.assertRegex(output
, routable_map
[carrier
])
2885 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2886 copy_network_unit('11-dummy.netdev')
2888 self
.wait_operstate('test1', 'off', '')
2889 check_output('ip link set dev test1 up carrier off')
2891 copy_network_unit('25-test1.network')
2893 self
.wait_online(['test1:no-carrier'])
2895 carrier_map
= {'on': '1', 'off': '0'}
2896 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2897 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2898 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2899 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2900 check_output(f
'ip link set dev test1 carrier {carrier}')
2901 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2903 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2906 self
.assertRegex(output
, '192.168.0.15')
2907 self
.assertRegex(output
, '192.168.0.1')
2909 self
.assertNotRegex(output
, '192.168.0.15')
2910 self
.assertNotRegex(output
, '192.168.0.1')
2911 self
.assertRegex(output
, routable_map
[carrier
])
2913 def test_routing_policy_rule(self
):
2914 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2916 self
.wait_online(['test1:degraded'])
2918 output
= check_output('ip rule list iif test1 priority 111')
2920 self
.assertRegex(output
, '111:')
2921 self
.assertRegex(output
, 'from 192.168.100.18')
2922 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2923 self
.assertRegex(output
, 'iif test1')
2924 self
.assertRegex(output
, 'oif test1')
2925 self
.assertRegex(output
, 'lookup 7')
2927 output
= check_output('ip rule list iif test1 priority 101')
2929 self
.assertRegex(output
, '101:')
2930 self
.assertRegex(output
, 'from all')
2931 self
.assertRegex(output
, 'iif test1')
2932 self
.assertRegex(output
, 'lookup 9')
2934 output
= check_output('ip -6 rule list iif test1 priority 100')
2936 self
.assertRegex(output
, '100:')
2937 self
.assertRegex(output
, 'from all')
2938 self
.assertRegex(output
, 'iif test1')
2939 self
.assertRegex(output
, 'lookup 8')
2941 output
= check_output('ip rule list iif test1 priority 102')
2943 self
.assertRegex(output
, '102:')
2944 self
.assertRegex(output
, 'from 0.0.0.0/8')
2945 self
.assertRegex(output
, 'iif test1')
2946 self
.assertRegex(output
, 'lookup 10')
2948 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2951 def test_routing_policy_rule_issue_11280(self
):
2952 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
2953 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
2955 for trial
in range(3):
2956 restart_networkd(show_logs
=(trial
> 0))
2957 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
2959 output
= check_output('ip rule list table 7')
2961 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2963 output
= check_output('ip rule list table 8')
2965 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2967 def test_routing_policy_rule_reconfigure(self
):
2968 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
2970 self
.wait_online(['test1:degraded'])
2972 output
= check_output('ip rule list table 1011')
2974 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2975 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2976 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2977 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2979 output
= check_output('ip -6 rule list table 1011')
2981 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2983 copy_network_unit('25-routing-policy-rule-reconfigure1.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
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
2997 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2999 call('ip rule delete priority 10111')
3000 call('ip rule delete priority 10112')
3001 call('ip rule delete priority 10113')
3002 call('ip rule delete priority 10114')
3003 call('ip -6 rule delete priority 10113')
3005 output
= check_output('ip rule list table 1011')
3007 self
.assertEqual(output
, '')
3009 output
= check_output('ip -6 rule list table 1011')
3011 self
.assertEqual(output
, '')
3013 networkctl_reconfigure('test1')
3014 self
.wait_online(['test1:degraded'])
3016 output
= check_output('ip rule list table 1011')
3018 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3019 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3020 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3021 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3023 output
= check_output('ip -6 rule list table 1011')
3025 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3027 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3028 def test_routing_policy_rule_port_range(self
):
3029 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3031 self
.wait_online(['test1:degraded'])
3033 output
= check_output('ip rule')
3035 self
.assertRegex(output
, '111')
3036 self
.assertRegex(output
, 'from 192.168.100.18')
3037 self
.assertRegex(output
, '1123-1150')
3038 self
.assertRegex(output
, '3224-3290')
3039 self
.assertRegex(output
, 'tcp')
3040 self
.assertRegex(output
, 'lookup 7')
3042 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3043 def test_routing_policy_rule_invert(self
):
3044 copy_network_unit('25-fibrule-invert.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
, 'not.*?from.*?192.168.100.18')
3052 self
.assertRegex(output
, 'tcp')
3053 self
.assertRegex(output
, 'lookup 7')
3055 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3056 def test_routing_policy_rule_uidrange(self
):
3057 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3059 self
.wait_online(['test1:degraded'])
3061 output
= check_output('ip rule')
3063 self
.assertRegex(output
, '111')
3064 self
.assertRegex(output
, 'from 192.168.100.18')
3065 self
.assertRegex(output
, 'lookup 7')
3066 self
.assertRegex(output
, 'uidrange 100-200')
3068 def _test_route_static(self
, manage_foreign_routes
):
3069 if not manage_foreign_routes
:
3070 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3072 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3073 '25-route-static-test1.network', '11-dummy.netdev')
3075 self
.wait_online(['dummy98:routable'])
3077 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3080 print('### ip -6 route show dev dummy98')
3081 output
= check_output('ip -6 route show dev dummy98')
3083 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3084 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3085 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3087 print('### ip -6 route show default')
3088 output
= check_output('ip -6 route show default')
3090 self
.assertIn('default', output
)
3091 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3093 print('### ip -4 route show dev dummy98')
3094 output
= check_output('ip -4 route show dev dummy98')
3096 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3097 self
.assertIn('149.10.124.64 proto static scope link', output
)
3098 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3099 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3100 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3101 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3102 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3103 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3105 print('### ip -4 route show dev dummy98 default')
3106 output
= check_output('ip -4 route show dev dummy98 default')
3108 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3109 self
.assertIn('default via 149.10.124.64 proto static', output
)
3110 self
.assertIn('default proto static', output
)
3111 self
.assertIn('default via 1.1.8.104 proto static', output
)
3113 print('### ip -4 route show table local dev dummy98')
3114 output
= check_output('ip -4 route show table local dev dummy98')
3116 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3117 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3118 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3120 print('### ip -4 route show type blackhole')
3121 output
= check_output('ip -4 route show type blackhole')
3123 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3125 print('### ip -4 route show type unreachable')
3126 output
= check_output('ip -4 route show type unreachable')
3128 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3130 print('### ip -4 route show type prohibit')
3131 output
= check_output('ip -4 route show type prohibit')
3133 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3135 print('### ip -6 route show type blackhole')
3136 output
= check_output('ip -6 route show type blackhole')
3138 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3140 print('### ip -6 route show type unreachable')
3141 output
= check_output('ip -6 route show type unreachable')
3143 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3145 print('### ip -6 route show type prohibit')
3146 output
= check_output('ip -6 route show type prohibit')
3148 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3150 print('### ip route show 192.168.10.1')
3151 output
= check_output('ip route show 192.168.10.1')
3153 self
.assertIn('192.168.10.1 proto static', output
)
3154 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3155 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3156 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3157 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3159 print('### ip route show 192.168.10.2')
3160 output
= check_output('ip route show 192.168.10.2')
3162 # old ip command does not show IPv6 gateways...
3163 self
.assertIn('192.168.10.2 proto static', output
)
3164 self
.assertIn('nexthop', output
)
3165 self
.assertIn('dev test1 weight 20', output
)
3166 self
.assertIn('dev test1 weight 30', output
)
3167 self
.assertIn('dev dummy98 weight 10', output
)
3168 self
.assertIn('dev dummy98 weight 5', output
)
3170 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3171 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3173 # old ip command does not show 'nexthop' keyword and weight...
3174 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3175 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3176 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3177 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3178 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3180 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3183 copy_network_unit('25-address-static.network')
3185 self
.wait_online(['dummy98:routable'])
3187 # check all routes managed by Manager are removed
3188 print('### ip -4 route show type blackhole')
3189 output
= check_output('ip -4 route show type blackhole')
3191 self
.assertEqual(output
, '')
3193 print('### ip -4 route show type unreachable')
3194 output
= check_output('ip -4 route show type unreachable')
3196 self
.assertEqual(output
, '')
3198 print('### ip -4 route show type prohibit')
3199 output
= check_output('ip -4 route show type prohibit')
3201 self
.assertEqual(output
, '')
3203 print('### ip -6 route show type blackhole')
3204 output
= check_output('ip -6 route show type blackhole')
3206 self
.assertEqual(output
, '')
3208 print('### ip -6 route show type unreachable')
3209 output
= check_output('ip -6 route show type unreachable')
3211 self
.assertEqual(output
, '')
3213 print('### ip -6 route show type prohibit')
3214 output
= check_output('ip -6 route show type prohibit')
3216 self
.assertEqual(output
, '')
3218 remove_network_unit('25-address-static.network')
3220 self
.wait_online(['dummy98:routable'])
3222 # check all routes managed by Manager are reconfigured
3223 print('### ip -4 route show type blackhole')
3224 output
= check_output('ip -4 route show type blackhole')
3226 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3228 print('### ip -4 route show type unreachable')
3229 output
= check_output('ip -4 route show type unreachable')
3231 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3233 print('### ip -4 route show type prohibit')
3234 output
= check_output('ip -4 route show type prohibit')
3236 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3238 print('### ip -6 route show type blackhole')
3239 output
= check_output('ip -6 route show type blackhole')
3241 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3243 print('### ip -6 route show type unreachable')
3244 output
= check_output('ip -6 route show type unreachable')
3246 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3248 print('### ip -6 route show type prohibit')
3249 output
= check_output('ip -6 route show type prohibit')
3251 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3253 remove_link('dummy98')
3256 # check all routes managed by Manager are removed
3257 print('### ip -4 route show type blackhole')
3258 output
= check_output('ip -4 route show type blackhole')
3260 self
.assertEqual(output
, '')
3262 print('### ip -4 route show type unreachable')
3263 output
= check_output('ip -4 route show type unreachable')
3265 self
.assertEqual(output
, '')
3267 print('### ip -4 route show type prohibit')
3268 output
= check_output('ip -4 route show type prohibit')
3270 self
.assertEqual(output
, '')
3272 print('### ip -6 route show type blackhole')
3273 output
= check_output('ip -6 route show type blackhole')
3275 self
.assertEqual(output
, '')
3277 print('### ip -6 route show type unreachable')
3278 output
= check_output('ip -6 route show type unreachable')
3280 self
.assertEqual(output
, '')
3282 print('### ip -6 route show type prohibit')
3283 output
= check_output('ip -6 route show type prohibit')
3285 self
.assertEqual(output
, '')
3287 def test_route_static(self
):
3289 for manage_foreign_routes
in [True, False]:
3295 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3296 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3297 self
._test
_route
_static
(manage_foreign_routes
)
3299 @expectedFailureIfRTA_VIAIsNotSupported()
3300 def test_route_via_ipv6(self
):
3301 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3303 self
.wait_online(['dummy98:routable'])
3305 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3308 print('### ip -6 route show dev dummy98')
3309 output
= check_output('ip -6 route show dev dummy98')
3311 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3312 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3314 print('### ip -4 route show dev dummy98')
3315 output
= check_output('ip -4 route show dev dummy98')
3317 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3318 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3320 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3321 def test_route_congctl(self
):
3322 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3324 self
.wait_online(['dummy98:routable'])
3326 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3327 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3329 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3330 self
.assertIn('congctl dctcp', output
)
3332 print('### ip -4 route show dev dummy98 149.10.124.66')
3333 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3335 self
.assertIn('149.10.124.66 proto static', output
)
3336 self
.assertIn('congctl dctcp', output
)
3337 self
.assertIn('rto_min 300s', output
)
3339 @expectedFailureIfModuleIsNotAvailable('vrf')
3340 def test_route_vrf(self
):
3341 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3342 '25-vrf.netdev', '25-vrf.network')
3344 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3346 output
= check_output('ip route show vrf vrf99')
3348 self
.assertRegex(output
, 'default via 192.168.100.1')
3350 output
= check_output('ip route show')
3352 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3354 def test_gateway_reconfigure(self
):
3355 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3357 self
.wait_online(['dummy98:routable'])
3358 print('### ip -4 route show dev dummy98 default')
3359 output
= check_output('ip -4 route show dev dummy98 default')
3361 self
.assertIn('default via 149.10.124.59 proto static', output
)
3362 self
.assertNotIn('149.10.124.60', output
)
3364 remove_network_unit('25-gateway-static.network')
3365 copy_network_unit('25-gateway-next-static.network')
3367 self
.wait_online(['dummy98:routable'])
3368 print('### ip -4 route show dev dummy98 default')
3369 output
= check_output('ip -4 route show dev dummy98 default')
3371 self
.assertNotIn('149.10.124.59', output
)
3372 self
.assertIn('default via 149.10.124.60 proto static', output
)
3374 def test_ip_route_ipv6_src_route(self
):
3375 # a dummy device does not make the addresses go through tentative state, so we
3376 # reuse a bond from an earlier test, which does make the addresses go through
3377 # tentative state, and do our test on that
3378 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3380 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3382 output
= check_output('ip -6 route list dev bond199')
3384 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3386 def test_route_preferred_source_with_existing_address(self
):
3388 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3393 networkctl_reconfigure('dummy98')
3395 self
.wait_online(['dummy98:routable'])
3397 output
= check_output('ip -6 route list dev dummy98')
3399 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3401 def test_ip_link_mac_address(self
):
3402 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3404 self
.wait_online(['dummy98:degraded'])
3406 output
= check_output('ip link show dummy98')
3408 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3410 def test_ip_link_unmanaged(self
):
3411 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3414 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3416 def test_ipv6_address_label(self
):
3417 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3419 self
.wait_online(['dummy98:degraded'])
3421 output
= check_output('ip addrlabel list')
3423 self
.assertRegex(output
, '2004:da8:1::/64')
3425 def test_ipv6_proxy_ndp(self
):
3426 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3429 self
.wait_online(['dummy98:routable'])
3431 output
= check_output('ip neighbor show proxy dev dummy98')
3433 for i
in range(1, 5):
3434 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3436 def test_neighbor_section(self
):
3437 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3439 self
.wait_online(['dummy98:degraded'])
3441 print('### ip neigh list dev dummy98')
3442 output
= check_output('ip neigh list dev dummy98')
3444 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3445 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3446 self
.assertNotIn('2004:da8:1:0::2', output
)
3447 self
.assertNotIn('192.168.10.2', output
)
3448 self
.assertNotIn('00:00:5e:00:02:67', output
)
3450 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3453 copy_network_unit('25-neighbor-section.network.d/override.conf')
3455 self
.wait_online(['dummy98:degraded'])
3457 print('### ip neigh list dev dummy98 (after reloading)')
3458 output
= check_output('ip neigh list dev dummy98')
3460 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3461 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3462 self
.assertNotIn('2004:da8:1:0::2', output
)
3463 self
.assertNotIn('192.168.10.2', output
)
3464 self
.assertNotIn('00:00:5e:00:02', output
)
3466 def test_neighbor_reconfigure(self
):
3467 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3469 self
.wait_online(['dummy98:degraded'])
3471 print('### ip neigh list dev dummy98')
3472 output
= check_output('ip neigh list dev dummy98')
3474 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3475 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3477 remove_network_unit('25-neighbor-section.network')
3478 copy_network_unit('25-neighbor-next.network')
3480 self
.wait_online(['dummy98:degraded'])
3481 print('### ip neigh list dev dummy98')
3482 output
= check_output('ip neigh list dev dummy98')
3484 self
.assertNotIn('00:00:5e:00:02:65', output
)
3485 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3486 self
.assertNotIn('2004:da8:1::1', output
)
3488 def test_neighbor_gre(self
):
3489 copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
3490 '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
3492 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout
='40s')
3494 output
= check_output('ip neigh list dev gretun97')
3496 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3497 self
.assertNotIn('10.0.0.23', output
)
3499 output
= check_output('ip neigh list dev ip6gretun97')
3501 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3502 self
.assertNotIn('2001:db8:0:f102::18', output
)
3504 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3507 def test_link_local_addressing(self
):
3508 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3509 '25-link-local-addressing-no.network', '12-dummy.netdev')
3511 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3513 output
= check_output('ip address show dev test1')
3515 self
.assertRegex(output
, 'inet .* scope link')
3516 self
.assertRegex(output
, 'inet6 .* scope link')
3518 output
= check_output('ip address show dev dummy98')
3520 self
.assertNotRegex(output
, 'inet6* .* scope link')
3522 # Documentation/networking/ip-sysctl.txt
3524 # addr_gen_mode - INTEGER
3525 # Defines how link-local and autoconf addresses are generated.
3527 # 0: generate address based on EUI64 (default)
3528 # 1: do no generate a link-local address, use EUI64 for addresses generated
3530 # 2: generate stable privacy addresses, using the secret from
3531 # stable_secret (RFC7217)
3532 # 3: generate stable privacy addresses, using a random secret if unset
3534 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3535 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3536 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3538 def test_link_local_addressing_ipv6ll(self
):
3539 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3541 self
.wait_online(['dummy98:degraded'])
3543 # An IPv6LL address exists by default.
3544 output
= check_output('ip address show dev dummy98')
3546 self
.assertRegex(output
, 'inet6 .* scope link')
3548 copy_network_unit('25-link-local-addressing-no.network')
3550 self
.wait_online(['dummy98:carrier'])
3552 # Check if the IPv6LL address is removed.
3553 output
= check_output('ip address show dev dummy98')
3555 self
.assertNotRegex(output
, 'inet6 .* scope link')
3557 remove_network_unit('25-link-local-addressing-no.network')
3559 self
.wait_online(['dummy98:degraded'])
3561 # Check if a new IPv6LL address is assigned.
3562 output
= check_output('ip address show dev dummy98')
3564 self
.assertRegex(output
, 'inet6 .* scope link')
3566 @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
3567 def test_sysctl(self
):
3568 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3569 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3571 self
.wait_online(['dummy98:degraded'])
3573 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3574 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3575 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3576 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3577 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3578 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3579 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3580 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3581 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3583 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3585 self
.wait_online(['dummy98:degraded'])
3587 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3589 def test_sysctl_disable_ipv6(self
):
3590 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3592 print('## Disable ipv6')
3593 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3594 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3597 self
.wait_online(['dummy98:routable'])
3599 output
= check_output('ip -4 address show dummy98')
3601 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3602 output
= check_output('ip -6 address show dummy98')
3604 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3605 self
.assertRegex(output
, 'inet6 .* scope link')
3606 output
= check_output('ip -4 route show dev dummy98')
3608 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3609 output
= check_output('ip -6 route show default')
3611 self
.assertRegex(output
, 'default')
3612 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3614 remove_link('dummy98')
3616 print('## Enable ipv6')
3617 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3618 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3621 self
.wait_online(['dummy98:routable'])
3623 output
= check_output('ip -4 address show dummy98')
3625 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3626 output
= check_output('ip -6 address show dummy98')
3628 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3629 self
.assertRegex(output
, 'inet6 .* scope link')
3630 output
= check_output('ip -4 route show dev dummy98')
3632 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3633 output
= check_output('ip -6 route show default')
3635 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3637 def test_bind_carrier(self
):
3638 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3641 # no bound interface.
3642 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3643 output
= check_output('ip address show test1')
3645 self
.assertNotIn('UP,LOWER_UP', output
)
3646 self
.assertIn('DOWN', output
)
3647 self
.assertNotIn('192.168.10', output
)
3649 # add one bound interface. The interface will be up.
3650 check_output('ip link add dummy98 type dummy')
3651 check_output('ip link set dummy98 up')
3652 self
.wait_online(['test1:routable'])
3653 output
= check_output('ip address show test1')
3655 self
.assertIn('UP,LOWER_UP', output
)
3656 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3658 # add another bound interface. The interface is still up.
3659 check_output('ip link add dummy99 type dummy')
3660 check_output('ip link set dummy99 up')
3661 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3662 output
= check_output('ip address show test1')
3664 self
.assertIn('UP,LOWER_UP', output
)
3665 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3667 # remove one of the bound interfaces. The interface is still up
3668 remove_link('dummy98')
3669 output
= check_output('ip address show test1')
3671 self
.assertIn('UP,LOWER_UP', output
)
3672 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3674 # bring down the remaining bound interface. The interface will be down.
3675 check_output('ip link set dummy99 down')
3676 self
.wait_operstate('test1', 'off')
3677 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3678 output
= check_output('ip address show test1')
3680 self
.assertNotIn('UP,LOWER_UP', output
)
3681 self
.assertIn('DOWN', output
)
3682 self
.assertNotIn('192.168.10', output
)
3684 # bring up the bound interface. The interface will be up.
3685 check_output('ip link set dummy99 up')
3686 self
.wait_online(['test1:routable'])
3687 output
= check_output('ip address show test1')
3689 self
.assertIn('UP,LOWER_UP', output
)
3690 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3692 # remove the remaining bound interface. The interface will be down.
3693 remove_link('dummy99')
3694 self
.wait_operstate('test1', 'off')
3695 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3696 output
= check_output('ip address show test1')
3698 self
.assertNotIn('UP,LOWER_UP', output
)
3699 self
.assertIn('DOWN', output
)
3700 self
.assertNotIn('192.168.10', output
)
3702 # re-add one bound interface. The interface will be up.
3703 check_output('ip link add dummy98 type dummy')
3704 check_output('ip link set dummy98 up')
3705 self
.wait_online(['test1:routable'])
3706 output
= check_output('ip address show test1')
3708 self
.assertIn('UP,LOWER_UP', output
)
3709 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3711 def _test_activation_policy(self
, interface
, test
):
3712 conffile
= '25-activation-policy.network'
3714 conffile
= f
'{conffile}.d/{test}.conf'
3715 if interface
== 'vlan99':
3716 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3717 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3720 always
= test
.startswith('always')
3721 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3722 expect_up
= initial_up
3723 next_up
= not expect_up
3725 if test
.endswith('down'):
3726 self
.wait_activated(interface
)
3728 for iteration
in range(4):
3729 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3730 operstate
= 'routable' if expect_up
else 'off'
3731 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3732 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3735 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3736 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3737 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3739 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3742 check_output(f
'ip link set dev {interface} up')
3744 check_output(f
'ip link set dev {interface} down')
3745 expect_up
= initial_up
if always
else next_up
3746 next_up
= not next_up
3750 def test_activation_policy(self
):
3752 for interface
in ['test1', 'vlan99']:
3753 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3759 print(f
'### test_activation_policy(interface={interface}, test={test})')
3760 with self
.subTest(interface
=interface
, test
=test
):
3761 self
._test
_activation
_policy
(interface
, test
)
3763 def _test_activation_policy_required_for_online(self
, policy
, required
):
3764 conffile
= '25-activation-policy.network'
3765 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3767 units
+= [f
'{conffile}.d/{policy}.conf']
3769 units
+= [f
'{conffile}.d/required-{required}.conf']
3770 copy_network_unit(*units
, copy_dropins
=False)
3773 if policy
.endswith('down'):
3774 self
.wait_activated('test1')
3776 if policy
.endswith('down') or policy
== 'manual':
3777 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3779 self
.wait_online(['test1'])
3781 if policy
== 'always-down':
3782 # if always-down, required for online is forced to no
3785 # otherwise if required for online is specified, it should match that
3786 expected
= required
== 'yes'
3788 # otherwise if only policy specified, required for online defaults to
3789 # true if policy is up, always-up, or bound
3790 expected
= policy
.endswith('up') or policy
== 'bound'
3792 # default is true, if neither are specified
3795 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
3798 yesno
= 'yes' if expected
else 'no'
3799 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3801 def test_activation_policy_required_for_online(self
):
3803 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3804 for required
in ['yes', 'no', '']:
3810 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3811 with self
.subTest(policy
=policy
, required
=required
):
3812 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3814 def test_domain(self
):
3815 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3817 self
.wait_online(['dummy98:routable'])
3819 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3821 self
.assertRegex(output
, 'Address: 192.168.42.100')
3822 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3823 self
.assertRegex(output
, 'Search Domains: one')
3825 def test_keep_configuration_static(self
):
3826 check_output('ip link add name dummy98 type dummy')
3827 check_output('ip address add 10.1.2.3/16 dev dummy98')
3828 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3829 output
= check_output('ip address show dummy98')
3831 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3832 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3833 output
= check_output('ip route show dev dummy98')
3836 copy_network_unit('24-keep-configuration-static.network')
3838 self
.wait_online(['dummy98:routable'])
3840 output
= check_output('ip address show dummy98')
3842 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3843 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3845 def check_nexthop(self
, manage_foreign_nexthops
):
3846 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3848 output
= check_output('ip nexthop list dev veth99')
3850 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3851 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3852 self
.assertIn('id 3 dev veth99', output
)
3853 self
.assertIn('id 4 dev veth99', output
)
3854 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3855 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3856 if manage_foreign_nexthops
:
3857 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3859 output
= check_output('ip nexthop list dev dummy98')
3861 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
3862 if manage_foreign_nexthops
:
3863 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
3865 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
3867 # kernel manages blackhole nexthops on lo
3868 output
= check_output('ip nexthop list dev lo')
3870 self
.assertIn('id 6 blackhole', output
)
3871 self
.assertIn('id 7 blackhole', output
)
3873 # group nexthops are shown with -0 option
3874 output
= check_output('ip -0 nexthop list id 21')
3876 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
3878 output
= check_output('ip route show dev veth99 10.10.10.10')
3880 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
3882 output
= check_output('ip route show dev veth99 10.10.10.11')
3884 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
3886 output
= check_output('ip route show dev veth99 10.10.10.12')
3888 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
3890 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3892 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3894 output
= check_output('ip route show 10.10.10.13')
3896 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
3898 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
3900 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
3902 output
= check_output('ip route show 10.10.10.14')
3904 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
3905 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
3906 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
3908 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3911 def _test_nexthop(self
, manage_foreign_nexthops
):
3912 if not manage_foreign_nexthops
:
3913 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
3915 check_output('ip link add dummy98 type dummy')
3916 check_output('ip link set dummy98 up')
3917 check_output('ip address add 192.168.20.20/24 dev dummy98')
3918 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
3920 copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
3921 '12-dummy.netdev', '25-nexthop-dummy.network')
3924 self
.check_nexthop(manage_foreign_nexthops
)
3926 remove_network_unit('25-nexthop.network')
3927 copy_network_unit('25-nexthop-nothing.network')
3929 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3931 output
= check_output('ip nexthop list dev veth99')
3933 self
.assertEqual(output
, '')
3934 output
= check_output('ip nexthop list dev lo')
3936 self
.assertEqual(output
, '')
3938 remove_network_unit('25-nexthop-nothing.network')
3939 copy_network_unit('25-nexthop.network')
3940 networkctl_reconfigure('dummy98')
3943 self
.check_nexthop(manage_foreign_nexthops
)
3945 remove_link('veth99')
3948 output
= check_output('ip nexthop list dev lo')
3950 self
.assertEqual(output
, '')
3952 @expectedFailureIfNexthopIsNotAvailable()
3953 def test_nexthop(self
):
3955 for manage_foreign_nexthops
in [True, False]:
3961 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
3962 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
3963 self
._test
_nexthop
(manage_foreign_nexthops
)
3965 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
3973 @expectedFailureIfModuleIsNotAvailable('sch_cake')
3974 def test_qdisc_cake(self
):
3975 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
3977 self
.wait_online(['dummy98:routable'])
3979 output
= check_output('tc qdisc show dev dummy98')
3981 self
.assertIn('qdisc cake 3a: root', output
)
3982 self
.assertIn('bandwidth 500Mbit', output
)
3983 self
.assertIn('autorate-ingress', output
)
3984 self
.assertIn('diffserv8', output
)
3985 self
.assertIn('dual-dsthost', output
)
3986 self
.assertIn(' nat', output
)
3987 self
.assertIn(' wash', output
)
3988 self
.assertIn(' split-gso', output
)
3989 self
.assertIn(' raw', output
)
3990 self
.assertIn(' atm', output
)
3991 self
.assertIn('overhead 128', output
)
3992 self
.assertIn('mpu 20', output
)
3993 self
.assertIn('fwmark 0xff00', output
)
3994 self
.assertIn('rtt 1s', output
)
3995 self
.assertIn('ack-filter-aggressive', output
)
3997 @expectedFailureIfModuleIsNotAvailable('sch_codel')
3998 def test_qdisc_codel(self
):
3999 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4001 self
.wait_online(['dummy98:routable'])
4003 output
= check_output('tc qdisc show dev dummy98')
4005 self
.assertRegex(output
, 'qdisc codel 33: root')
4006 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4008 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4009 def test_qdisc_drr(self
):
4010 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4012 self
.wait_online(['dummy98:routable'])
4014 output
= check_output('tc qdisc show dev dummy98')
4016 self
.assertRegex(output
, 'qdisc drr 2: root')
4017 output
= check_output('tc class show dev dummy98')
4019 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4021 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4022 def test_qdisc_ets(self
):
4023 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4025 self
.wait_online(['dummy98:routable'])
4027 output
= check_output('tc qdisc show dev dummy98')
4030 self
.assertRegex(output
, 'qdisc ets 3a: root')
4031 self
.assertRegex(output
, 'bands 10 strict 3')
4032 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4033 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4035 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4036 def test_qdisc_fq(self
):
4037 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4039 self
.wait_online(['dummy98:routable'])
4041 output
= check_output('tc qdisc show dev dummy98')
4043 self
.assertRegex(output
, 'qdisc fq 32: root')
4044 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4045 self
.assertRegex(output
, 'quantum 1500')
4046 self
.assertRegex(output
, 'initial_quantum 13000')
4047 self
.assertRegex(output
, 'maxrate 1Mbit')
4049 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4050 def test_qdisc_fq_codel(self
):
4051 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4053 self
.wait_online(['dummy98:routable'])
4055 output
= check_output('tc qdisc show dev dummy98')
4057 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4058 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')
4060 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4061 def test_qdisc_fq_pie(self
):
4062 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4064 self
.wait_online(['dummy98:routable'])
4066 output
= check_output('tc qdisc show dev dummy98')
4069 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4070 self
.assertRegex(output
, 'limit 200000p')
4072 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4073 def test_qdisc_gred(self
):
4074 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4076 self
.wait_online(['dummy98:routable'])
4078 output
= check_output('tc qdisc show dev dummy98')
4080 self
.assertRegex(output
, 'qdisc gred 38: root')
4081 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4083 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4084 def test_qdisc_hhf(self
):
4085 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4087 self
.wait_online(['dummy98:routable'])
4089 output
= check_output('tc qdisc show dev dummy98')
4091 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4092 self
.assertRegex(output
, 'limit 1022p')
4094 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4095 def test_qdisc_htb_fifo(self
):
4096 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4098 self
.wait_online(['dummy98:routable'])
4100 output
= check_output('tc qdisc show dev dummy98')
4102 self
.assertRegex(output
, 'qdisc htb 2: root')
4103 self
.assertRegex(output
, r
'default (0x30|30)')
4105 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4106 self
.assertRegex(output
, 'limit 100000p')
4108 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4109 self
.assertRegex(output
, 'limit 1000000')
4111 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4112 self
.assertRegex(output
, 'limit 1023p')
4114 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4116 output
= check_output('tc -d class show dev dummy98')
4118 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4119 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4120 # which is fixed in v6.3.0 by
4121 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4122 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4123 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4124 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4125 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4126 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4127 self
.assertRegex(output
, 'burst 123456')
4128 self
.assertRegex(output
, 'cburst 123457')
4130 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4131 def test_qdisc_ingress(self
):
4132 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4133 '25-qdisc-ingress.network', '11-dummy.netdev')
4135 self
.wait_online(['dummy98:routable', 'test1:routable'])
4137 output
= check_output('tc qdisc show dev dummy98')
4139 self
.assertRegex(output
, 'qdisc clsact')
4141 output
= check_output('tc qdisc show dev test1')
4143 self
.assertRegex(output
, 'qdisc ingress')
4145 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4146 def test_qdisc_netem(self
):
4147 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4148 '25-qdisc-netem-compat.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 netem 30: root')
4155 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4157 output
= check_output('tc qdisc show dev test1')
4159 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4160 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4162 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4163 def test_qdisc_pie(self
):
4164 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4166 self
.wait_online(['dummy98:routable'])
4168 output
= check_output('tc qdisc show dev dummy98')
4170 self
.assertRegex(output
, 'qdisc pie 3a: root')
4171 self
.assertRegex(output
, 'limit 200000')
4173 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4174 def test_qdisc_qfq(self
):
4175 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4177 self
.wait_online(['dummy98:routable'])
4179 output
= check_output('tc qdisc show dev dummy98')
4181 self
.assertRegex(output
, 'qdisc qfq 2: root')
4182 output
= check_output('tc class show dev dummy98')
4184 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4185 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4187 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4188 def test_qdisc_sfb(self
):
4189 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4191 self
.wait_online(['dummy98:routable'])
4193 output
= check_output('tc qdisc show dev dummy98')
4195 self
.assertRegex(output
, 'qdisc sfb 39: root')
4196 self
.assertRegex(output
, 'limit 200000')
4198 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4199 def test_qdisc_sfq(self
):
4200 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4202 self
.wait_online(['dummy98:routable'])
4204 output
= check_output('tc qdisc show dev dummy98')
4206 self
.assertRegex(output
, 'qdisc sfq 36: root')
4207 self
.assertRegex(output
, 'perturb 5sec')
4209 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4210 def test_qdisc_tbf(self
):
4211 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4213 self
.wait_online(['dummy98:routable'])
4215 output
= check_output('tc qdisc show dev dummy98')
4217 self
.assertRegex(output
, 'qdisc tbf 35: root')
4218 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4220 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4221 def test_qdisc_teql(self
):
4222 call_quiet('rmmod sch_teql')
4224 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4226 self
.wait_links('dummy98')
4227 check_output('modprobe sch_teql max_equalizers=2')
4228 self
.wait_online(['dummy98:routable'])
4230 output
= check_output('tc qdisc show dev dummy98')
4232 self
.assertRegex(output
, 'qdisc teql1 31: root')
4234 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4242 def test_state_file(self
):
4243 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4245 self
.wait_online(['dummy98:routable'])
4247 # make link state file updated
4248 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4250 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4253 output
= read_link_state_file('dummy98')
4255 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4256 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4257 self
.assertIn('ADMIN_STATE=configured', output
)
4258 self
.assertIn('OPER_STATE=routable', output
)
4259 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4260 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4261 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4262 self
.assertIn('ACTIVATION_POLICY=up', output
)
4263 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4264 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4265 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4266 self
.assertIn('DOMAINS=hogehoge', output
)
4267 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4268 self
.assertIn('LLMNR=no', output
)
4269 self
.assertIn('MDNS=yes', output
)
4270 self
.assertIn('DNSSEC=no', output
)
4272 check_output(*resolvectl_cmd
, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env
=env
)
4273 check_output(*resolvectl_cmd
, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env
=env
)
4274 check_output(*resolvectl_cmd
, 'llmnr', 'dummy98', 'yes', env
=env
)
4275 check_output(*resolvectl_cmd
, 'mdns', 'dummy98', 'no', env
=env
)
4276 check_output(*resolvectl_cmd
, 'dnssec', 'dummy98', 'yes', env
=env
)
4277 check_output(*timedatectl_cmd
, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env
=env
)
4279 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4282 output
= read_link_state_file('dummy98')
4284 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4285 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4286 self
.assertIn('DOMAINS=hogehogehoge', output
)
4287 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4288 self
.assertIn('LLMNR=yes', output
)
4289 self
.assertIn('MDNS=no', output
)
4290 self
.assertIn('DNSSEC=yes', output
)
4292 check_output(*timedatectl_cmd
, 'revert', 'dummy98', 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=0.fedora.pool.ntp.org 1.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(*resolvectl_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.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4315 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4316 self
.assertIn('DOMAINS=hogehoge', output
)
4317 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4318 self
.assertIn('LLMNR=no', output
)
4319 self
.assertIn('MDNS=yes', output
)
4320 self
.assertIn('DNSSEC=no', output
)
4322 def test_address_state(self
):
4323 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4326 self
.wait_online(['dummy98:degraded'])
4328 output
= read_link_state_file('dummy98')
4329 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4330 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4332 # with a routable IPv4 address
4333 check_output('ip address add 10.1.2.3/16 dev dummy98')
4334 self
.wait_online(['dummy98:routable'], ipv4
=True)
4335 self
.wait_online(['dummy98:routable'])
4337 output
= read_link_state_file('dummy98')
4338 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4339 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4341 check_output('ip address del 10.1.2.3/16 dev dummy98')
4343 # with a routable IPv6 address
4344 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4345 self
.wait_online(['dummy98:routable'], ipv6
=True)
4346 self
.wait_online(['dummy98:routable'])
4348 output
= read_link_state_file('dummy98')
4349 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4350 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4352 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4360 def test_bond_keep_master(self
):
4361 check_output('ip link add bond199 type bond mode active-backup')
4362 check_output('ip link add dummy98 type dummy')
4363 check_output('ip link set dummy98 master bond199')
4365 copy_network_unit('23-keep-master.network')
4367 self
.wait_online(['dummy98:enslaved'])
4369 output
= check_output('ip -d link show bond199')
4371 self
.assertRegex(output
, 'active_slave dummy98')
4373 output
= check_output('ip -d link show dummy98')
4375 self
.assertRegex(output
, 'master bond199')
4377 def test_bond_active_slave(self
):
4378 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4380 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4382 output
= check_output('ip -d link show bond199')
4384 self
.assertIn('active_slave dummy98', output
)
4386 def test_bond_primary_slave(self
):
4387 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4389 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4391 output
= check_output('ip -d link show bond199')
4393 self
.assertIn('primary dummy98', output
)
4396 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4397 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4398 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4399 f
.write(f
'[Link]\nMACAddress={mac}\n')
4402 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4404 output
= check_output('ip -d link show bond199')
4406 self
.assertIn(f
'link/ether {mac}', output
)
4408 def test_bond_operstate(self
):
4409 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4410 '25-bond99.network', '25-bond-slave.network')
4412 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4414 output
= check_output('ip -d link show dummy98')
4416 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4418 output
= check_output('ip -d link show test1')
4420 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4422 output
= check_output('ip -d link show bond99')
4424 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4426 self
.wait_operstate('dummy98', 'enslaved')
4427 self
.wait_operstate('test1', 'enslaved')
4428 self
.wait_operstate('bond99', 'routable')
4430 check_output('ip link set dummy98 down')
4432 self
.wait_operstate('dummy98', 'off')
4433 self
.wait_operstate('test1', 'enslaved')
4434 self
.wait_operstate('bond99', 'routable')
4436 check_output('ip link set dummy98 up')
4438 self
.wait_operstate('dummy98', 'enslaved')
4439 self
.wait_operstate('test1', 'enslaved')
4440 self
.wait_operstate('bond99', 'routable')
4442 check_output('ip link set dummy98 down')
4443 check_output('ip link set test1 down')
4445 self
.wait_operstate('dummy98', 'off')
4446 self
.wait_operstate('test1', 'off')
4448 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4449 # Huh? Kernel does not recognize that all slave interfaces are down?
4450 # Let's confirm that networkd's operstate is consistent with ip's result.
4451 self
.assertNotRegex(output
, 'NO-CARRIER')
4453 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4461 def test_bridge_vlan(self
):
4462 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4463 '26-bridge.netdev', '26-bridge-vlan-master.network',
4466 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4468 output
= check_output('bridge vlan show dev test1')
4470 # check if the default VID is removed
4471 self
.assertNotIn('1 Egress Untagged', output
)
4472 for i
in range(1000, 3000):
4474 self
.assertIn(f
'{i} PVID', output
)
4475 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4476 self
.assertIn(f
'{i} Egress Untagged', output
)
4477 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4478 self
.assertIn(f
'{i}', output
)
4480 self
.assertNotIn(f
'{i}', output
)
4482 output
= check_output('bridge vlan show dev bridge99')
4484 # check if the default VID is removed
4485 self
.assertNotIn('1 Egress Untagged', output
)
4486 for i
in range(1000, 3000):
4488 self
.assertIn(f
'{i} PVID', output
)
4489 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4490 self
.assertIn(f
'{i} Egress Untagged', output
)
4491 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4492 self
.assertIn(f
'{i}', output
)
4494 self
.assertNotIn(f
'{i}', output
)
4497 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4498 '26-bridge-vlan-master.network.d/10-override.conf')
4500 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4502 output
= check_output('bridge vlan show dev test1')
4504 for i
in range(1000, 3000):
4506 self
.assertIn(f
'{i} PVID', output
)
4507 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4508 self
.assertIn(f
'{i} Egress Untagged', output
)
4509 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4510 self
.assertIn(f
'{i}', output
)
4512 self
.assertNotIn(f
'{i}', output
)
4514 output
= check_output('bridge vlan show dev bridge99')
4516 for i
in range(1000, 3000):
4518 self
.assertIn(f
'{i} PVID', output
)
4519 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4520 self
.assertIn(f
'{i} Egress Untagged', output
)
4521 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4522 self
.assertIn(f
'{i}', output
)
4524 self
.assertNotIn(f
'{i}', output
)
4526 # Remove several vlan IDs
4527 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4528 '26-bridge-vlan-master.network.d/20-override.conf')
4530 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4532 output
= check_output('bridge vlan show dev test1')
4534 for i
in range(1000, 3000):
4536 self
.assertIn(f
'{i} PVID', output
)
4537 elif i
in range(2012, 2016):
4538 self
.assertIn(f
'{i} Egress Untagged', output
)
4539 elif i
in range(2008, 2014):
4540 self
.assertIn(f
'{i}', output
)
4542 self
.assertNotIn(f
'{i}', output
)
4544 output
= check_output('bridge vlan show dev bridge99')
4546 for i
in range(1000, 3000):
4548 self
.assertIn(f
'{i} PVID', output
)
4549 elif i
in range(2022, 2026):
4550 self
.assertIn(f
'{i} Egress Untagged', output
)
4551 elif i
in range(2018, 2024):
4552 self
.assertIn(f
'{i}', output
)
4554 self
.assertNotIn(f
'{i}', output
)
4556 # Remove all vlan IDs
4557 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4558 '26-bridge-vlan-master.network.d/30-override.conf')
4560 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4562 output
= check_output('bridge vlan show dev test1')
4564 self
.assertNotIn('PVID', output
)
4565 for i
in range(1000, 3000):
4566 self
.assertNotIn(f
'{i}', output
)
4568 output
= check_output('bridge vlan show dev bridge99')
4570 self
.assertNotIn('PVID', output
)
4571 for i
in range(1000, 3000):
4572 self
.assertNotIn(f
'{i}', output
)
4574 def test_bridge_vlan_issue_20373(self
):
4575 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4576 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4577 '21-vlan.netdev', '21-vlan.network')
4579 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4581 output
= check_output('bridge vlan show dev test1')
4583 self
.assertIn('100 PVID Egress Untagged', output
)
4584 self
.assertIn('560', output
)
4585 self
.assertIn('600', output
)
4587 output
= check_output('bridge vlan show dev bridge99')
4589 self
.assertIn('1 PVID Egress Untagged', output
)
4590 self
.assertIn('100', output
)
4591 self
.assertIn('600', output
)
4593 def test_bridge_mdb(self
):
4594 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4595 '26-bridge.netdev', '26-bridge-mdb-master.network')
4597 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4599 output
= check_output('bridge mdb show dev bridge99')
4601 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4602 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4604 # Old kernel may not support bridge MDB entries on bridge master
4605 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4606 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4607 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4609 def test_bridge_keep_master(self
):
4610 check_output('ip link add bridge99 type bridge')
4611 check_output('ip link set bridge99 up')
4612 check_output('ip link add dummy98 type dummy')
4613 check_output('ip link set dummy98 master bridge99')
4615 copy_network_unit('23-keep-master.network')
4617 self
.wait_online(['dummy98:enslaved'])
4619 output
= check_output('ip -d link show dummy98')
4621 self
.assertRegex(output
, 'master bridge99')
4622 self
.assertRegex(output
, 'bridge')
4624 output
= check_output('bridge -d link show dummy98')
4626 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4627 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4628 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4629 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4630 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4631 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4632 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4633 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4634 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4635 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4636 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4637 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4639 def test_bridge_property(self
):
4640 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4641 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4642 '25-bridge99.network')
4644 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4646 output
= check_output('ip -d link show bridge99')
4648 self
.assertIn('mtu 9000 ', output
)
4650 output
= check_output('ip -d link show test1')
4652 self
.assertIn('master bridge99 ', output
)
4653 self
.assertIn('bridge_slave', output
)
4654 self
.assertIn('mtu 9000 ', output
)
4656 output
= check_output('ip -d link show dummy98')
4658 self
.assertIn('master bridge99 ', output
)
4659 self
.assertIn('bridge_slave', output
)
4660 self
.assertIn('mtu 9000 ', output
)
4662 output
= check_output('ip addr show bridge99')
4664 self
.assertIn('192.168.0.15/24', output
)
4666 output
= check_output('bridge -d link show dummy98')
4668 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4669 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4670 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4671 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4672 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4673 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4674 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4675 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4676 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4677 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4678 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4679 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4680 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4682 output
= check_output('bridge -d link show test1')
4684 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4686 check_output('ip address add 192.168.0.16/24 dev bridge99')
4687 output
= check_output('ip addr show bridge99')
4689 self
.assertIn('192.168.0.16/24', output
)
4692 print('### ip -6 route list table all dev bridge99')
4693 output
= check_output('ip -6 route list table all dev bridge99')
4695 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4697 remove_link('test1')
4698 self
.wait_operstate('bridge99', 'routable')
4700 output
= check_output('ip -d link show bridge99')
4702 self
.assertIn('mtu 9000 ', output
)
4704 output
= check_output('ip -d link show dummy98')
4706 self
.assertIn('master bridge99 ', output
)
4707 self
.assertIn('bridge_slave', output
)
4708 self
.assertIn('mtu 9000 ', output
)
4710 remove_link('dummy98')
4711 self
.wait_operstate('bridge99', 'no-carrier')
4713 output
= check_output('ip -d link show bridge99')
4715 # When no carrier, the kernel may reset the MTU
4716 self
.assertIn('NO-CARRIER', output
)
4718 output
= check_output('ip address show bridge99')
4720 self
.assertNotIn('192.168.0.15/24', output
)
4721 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4723 print('### ip -6 route list table all dev bridge99')
4724 output
= check_output('ip -6 route list table all dev bridge99')
4726 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4728 check_output('ip link add dummy98 type dummy')
4729 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4731 output
= check_output('ip -d link show bridge99')
4733 self
.assertIn('mtu 9000 ', output
)
4735 output
= check_output('ip -d link show dummy98')
4737 self
.assertIn('master bridge99 ', output
)
4738 self
.assertIn('bridge_slave', output
)
4739 self
.assertIn('mtu 9000 ', output
)
4741 def test_bridge_configure_without_carrier(self
):
4742 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4746 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4747 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4748 with self
.subTest(test
=test
):
4749 if test
== 'no-slave':
4750 # bridge has no slaves; it's up but *might* not have carrier
4751 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4752 # due to a bug in the kernel, newly-created bridges are brought up
4753 # *with* carrier, unless they have had any setting changed; e.g.
4754 # their mac set, priority set, etc. Then, they will lose carrier
4755 # as soon as a (down) slave interface is added, and regain carrier
4756 # again once the slave interface is brought up.
4757 #self.check_link_attr('bridge99', 'carrier', '0')
4758 elif test
== 'add-slave':
4759 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4760 self
.check_link_attr('test1', 'operstate', 'down')
4761 check_output('ip link set dev test1 master bridge99')
4762 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4763 self
.check_link_attr('bridge99', 'carrier', '0')
4764 elif test
== 'slave-up':
4765 # bring up slave, which will have carrier; bridge gains carrier
4766 check_output('ip link set dev test1 up')
4767 self
.wait_online(['bridge99:routable'])
4768 self
.check_link_attr('bridge99', 'carrier', '1')
4769 elif test
== 'slave-no-carrier':
4770 # drop slave carrier; bridge loses carrier
4771 check_output('ip link set dev test1 carrier off')
4772 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4773 self
.check_link_attr('bridge99', 'carrier', '0')
4774 elif test
== 'slave-carrier':
4775 # restore slave carrier; bridge gains carrier
4776 check_output('ip link set dev test1 carrier on')
4777 self
.wait_online(['bridge99:routable'])
4778 self
.check_link_attr('bridge99', 'carrier', '1')
4779 elif test
== 'slave-down':
4780 # bring down slave; bridge loses carrier
4781 check_output('ip link set dev test1 down')
4782 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4783 self
.check_link_attr('bridge99', 'carrier', '0')
4785 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
4786 self
.assertRegex(output
, '10.1.2.3')
4787 self
.assertRegex(output
, '10.1.2.1')
4789 def test_bridge_ignore_carrier_loss(self
):
4790 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4791 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4792 '25-bridge99-ignore-carrier-loss.network')
4794 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4796 check_output('ip address add 192.168.0.16/24 dev bridge99')
4797 remove_link('test1', 'dummy98')
4800 output
= check_output('ip address show bridge99')
4802 self
.assertRegex(output
, 'NO-CARRIER')
4803 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4804 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
4806 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
4807 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4808 '25-bridge99-ignore-carrier-loss.network')
4810 self
.wait_online(['bridge99:no-carrier'])
4812 for trial
in range(4):
4813 check_output('ip link add dummy98 type dummy')
4814 check_output('ip link set dummy98 up')
4816 remove_link('dummy98')
4818 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4820 output
= check_output('ip address show bridge99')
4822 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4824 output
= check_output('ip rule list table 100')
4826 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
4828 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
4836 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4837 def test_sriov(self
):
4838 copy_network_unit('25-default.link', '25-sriov.network')
4840 call('modprobe netdevsim')
4842 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4845 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
4849 self
.wait_online(['eni99np1:routable'])
4851 output
= check_output('ip link show dev eni99np1')
4853 self
.assertRegex(output
,
4854 '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 *'
4855 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4856 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4859 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4860 def test_sriov_udev(self
):
4861 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
4863 call('modprobe netdevsim')
4865 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4869 self
.wait_online(['eni99np1:routable'])
4871 # the name eni99np1 may be an alternative name.
4872 ifname
= link_resolve('eni99np1')
4874 output
= check_output('ip link show dev eni99np1')
4876 self
.assertRegex(output
,
4877 '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 *'
4878 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4879 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4881 self
.assertNotIn('vf 3', output
)
4882 self
.assertNotIn('vf 4', output
)
4884 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4885 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
4888 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4890 output
= check_output('ip link show dev eni99np1')
4892 self
.assertRegex(output
,
4893 '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 *'
4894 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4895 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4898 self
.assertNotIn('vf 4', output
)
4900 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4901 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4904 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4906 output
= check_output('ip link show dev eni99np1')
4908 self
.assertRegex(output
,
4909 '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 *'
4910 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4911 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4914 self
.assertNotIn('vf 4', output
)
4916 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4917 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
4920 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4922 output
= check_output('ip link show dev eni99np1')
4924 self
.assertRegex(output
,
4925 '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 *'
4926 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
4928 self
.assertNotIn('vf 2', output
)
4929 self
.assertNotIn('vf 3', output
)
4930 self
.assertNotIn('vf 4', output
)
4932 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4933 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4936 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4938 output
= check_output('ip link show dev eni99np1')
4940 self
.assertRegex(output
,
4941 '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 *'
4942 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4943 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4945 self
.assertNotIn('vf 3', output
)
4946 self
.assertNotIn('vf 4', output
)
4948 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
4956 def test_lldp(self
):
4957 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
4959 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
4961 for trial
in range(10):
4965 output
= check_output(*networkctl_cmd
, 'lldp', env
=env
)
4967 if re
.search(r
'veth99 .* veth-peer', output
):
4972 class NetworkdRATests(unittest
.TestCase
, Utilities
):
4980 def test_ipv6_prefix_delegation(self
):
4981 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
4982 self
.setup_nftset('addr6', 'ipv6_addr')
4983 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
4984 self
.setup_nftset('ifindex', 'iface_index')
4986 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4988 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4990 self
.assertRegex(output
, 'fe80::')
4991 self
.assertRegex(output
, '2002:da8:1::1')
4993 output
= check_output(*resolvectl_cmd
, 'domain', 'veth99', env
=env
)
4995 self
.assertIn('hogehoge.test', output
)
4997 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4999 self
.assertRegex(output
, '2002:da8:1:0')
5001 self
.check_netlabel('veth99', '2002:da8:1::/64')
5002 self
.check_netlabel('veth99', '2002:da8:2::/64')
5004 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5005 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5006 self
.check_nftset('network6', '2002:da8:1::/64')
5007 self
.check_nftset('network6', '2002:da8:2::/64')
5008 self
.check_nftset('ifindex', 'veth99')
5010 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5012 def test_ipv6_token_static(self
):
5013 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5015 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5017 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5019 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5020 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5021 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5022 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5024 def test_ipv6_token_prefixstable(self
):
5025 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5027 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5029 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5031 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5032 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5034 def test_ipv6_token_prefixstable_without_address(self
):
5035 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5037 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5039 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5041 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5042 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5044 def test_router_preference(self
):
5045 copy_network_unit('25-veth-client.netdev',
5046 '25-veth-router-high.netdev',
5047 '25-veth-router-low.netdev',
5049 '25-veth-bridge.network',
5050 '25-veth-client.network',
5051 '25-veth-router-high.network',
5052 '25-veth-router-low.network',
5053 '25-bridge99.network')
5055 self
.wait_online(['client-p:enslaved',
5056 'router-high:degraded', 'router-high-p:enslaved',
5057 'router-low:degraded', 'router-low-p:enslaved',
5058 'bridge99:routable'])
5060 networkctl_reconfigure('client')
5061 self
.wait_online(['client:routable'])
5063 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5064 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5065 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5066 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5068 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5070 self
.assertIn('pref high', output
)
5071 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5073 self
.assertIn('pref low', output
)
5075 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5076 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5079 self
.wait_online(['client:routable'])
5081 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5082 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5083 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5084 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5086 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5088 self
.assertIn('pref high', output
)
5089 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5091 self
.assertIn('pref low', output
)
5093 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5094 def test_captive_portal(self
):
5095 copy_network_unit('25-veth-client.netdev',
5096 '25-veth-router-captive.netdev',
5098 '25-veth-client-captive.network',
5099 '25-veth-router-captive.network',
5100 '25-veth-bridge-captive.network',
5101 '25-bridge99.network')
5103 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5104 'router-captive:degraded', 'router-captivep:enslaved'])
5106 start_radvd(config_file
='captive-portal.conf')
5107 networkctl_reconfigure('client')
5108 self
.wait_online(['client:routable'])
5110 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5111 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5113 self
.assertIn('Captive Portal: http://systemd.io', output
)
5115 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5116 def test_invalid_captive_portal(self
):
5117 def radvd_write_config(captive_portal_uri
):
5118 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5119 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5121 captive_portal_uris
= [
5122 "42ěščěškd ěšč ě s",
5127 copy_network_unit('25-veth-client.netdev',
5128 '25-veth-router-captive.netdev',
5130 '25-veth-client-captive.network',
5131 '25-veth-router-captive.network',
5132 '25-veth-bridge-captive.network',
5133 '25-bridge99.network')
5135 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5136 'router-captive:degraded', 'router-captivep:enslaved'])
5138 for uri
in captive_portal_uris
:
5139 print(f
"Captive portal: {uri}")
5140 radvd_write_config(uri
)
5142 start_radvd(config_file
='bogus-captive-portal.conf')
5143 networkctl_reconfigure('client')
5144 self
.wait_online(['client:routable'])
5146 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5147 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5149 self
.assertNotIn('Captive Portal:', output
)
5151 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5159 def test_dhcp_server(self
):
5160 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5162 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5164 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5166 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5167 self
.assertIn('Gateway: 192.168.5.3', output
)
5168 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5169 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5171 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5172 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5174 def test_dhcp_server_null_server_address(self
):
5175 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5177 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5179 output
= check_output('ip --json address show dev veth-peer')
5180 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5181 print(server_address
)
5183 output
= check_output('ip --json address show dev veth99')
5184 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5185 print(client_address
)
5187 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5189 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5190 self
.assertIn(f
'Gateway: {server_address}', output
)
5191 self
.assertIn(f
'DNS: {server_address}', output
)
5192 self
.assertIn(f
'NTP: {server_address}', output
)
5194 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5195 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5197 def test_dhcp_server_with_uplink(self
):
5198 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5199 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5201 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5203 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5205 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5206 self
.assertIn('Gateway: 192.168.5.3', output
)
5207 self
.assertIn('DNS: 192.168.5.1', output
)
5208 self
.assertIn('NTP: 192.168.5.1', output
)
5210 def test_emit_router_timezone(self
):
5211 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5213 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5215 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5217 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5218 self
.assertIn('Gateway: 192.168.5.1', output
)
5219 self
.assertIn('Time Zone: Europe/Berlin', output
)
5221 def test_dhcp_server_static_lease(self
):
5222 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5224 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5226 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5228 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5229 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5231 def test_dhcp_server_static_lease_default_client_id(self
):
5232 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5234 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5236 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5238 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5239 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5241 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5249 def test_relay_agent(self
):
5250 copy_network_unit('25-agent-veth-client.netdev',
5251 '25-agent-veth-server.netdev',
5252 '25-agent-client.network',
5253 '25-agent-server.network',
5254 '25-agent-client-peer.network',
5255 '25-agent-server-peer.network')
5258 self
.wait_online(['client:routable'])
5260 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'client', env
=env
)
5262 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5264 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5272 def test_dhcp_client_ipv6_only(self
):
5273 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5276 self
.wait_online(['veth-peer:carrier'])
5278 # information request mode
5279 # The name ipv6-only option may not be supported by older dnsmasq
5280 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5281 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5282 '--dhcp-option=option6:dns-server,[2600::ee]',
5283 '--dhcp-option=option6:ntp-server,[2600::ff]',
5284 ra_mode
='ra-stateless')
5285 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5287 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5288 # Let's wait for the expected DNS server being listed in the state file.
5289 for _
in range(100):
5290 output
= read_link_state_file('veth99')
5291 if 'DNS=2600::ee' in output
:
5295 # Check link state file
5296 print('## link state file')
5297 output
= read_link_state_file('veth99')
5299 self
.assertIn('DNS=2600::ee', output
)
5300 self
.assertIn('NTP=2600::ff', output
)
5302 # Check manager state file
5303 print('## manager state file')
5304 output
= read_manager_state_file()
5306 self
.assertRegex(output
, 'DNS=.*2600::ee')
5307 self
.assertRegex(output
, 'NTP=.*2600::ff')
5309 print('## dnsmasq log')
5310 output
= read_dnsmasq_log_file()
5312 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5313 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5314 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5315 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5316 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5319 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5324 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5325 '--dhcp-option=option6:dns-server,[2600::ee]',
5326 '--dhcp-option=option6:ntp-server,[2600::ff]')
5327 networkctl_reconfigure('veth99')
5328 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5331 output
= check_output('ip address show dev veth99 scope global')
5333 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5334 self
.assertNotIn('192.168.5', output
)
5336 # checking semi-static route
5337 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5339 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5341 # Confirm that ipv6 token is not set in the kernel
5342 output
= check_output('ip token show dev veth99')
5344 self
.assertRegex(output
, 'token :: dev veth99')
5346 # Make manager and link state file updated
5347 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5349 # Check link state file
5350 print('## link state file')
5351 output
= read_link_state_file('veth99')
5353 self
.assertIn('DNS=2600::ee', output
)
5354 self
.assertIn('NTP=2600::ff', output
)
5356 # Check manager state file
5357 print('## manager state file')
5358 output
= read_manager_state_file()
5360 self
.assertRegex(output
, 'DNS=.*2600::ee')
5361 self
.assertRegex(output
, 'NTP=.*2600::ff')
5363 print('## dnsmasq log')
5364 output
= read_dnsmasq_log_file()
5366 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5367 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5368 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5369 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5370 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5371 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5374 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5377 # Testing without rapid commit support
5378 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5379 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5382 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5383 '--dhcp-option=option6:dns-server,[2600::ee]',
5384 '--dhcp-option=option6:ntp-server,[2600::ff]')
5387 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5390 output
= check_output('ip address show dev veth99 scope global')
5392 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5393 self
.assertNotIn('192.168.5', output
)
5395 # checking semi-static route
5396 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5398 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5400 # Make manager and link state file updated
5401 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5403 # Check link state file
5404 print('## link state file')
5405 output
= read_link_state_file('veth99')
5407 self
.assertIn('DNS=2600::ee', output
)
5408 self
.assertIn('NTP=2600::ff', output
)
5410 # Check manager state file
5411 print('## manager state file')
5412 output
= read_manager_state_file()
5414 self
.assertRegex(output
, 'DNS=.*2600::ee')
5415 self
.assertRegex(output
, 'NTP=.*2600::ff')
5417 print('## dnsmasq log')
5418 output
= read_dnsmasq_log_file()
5420 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5421 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5422 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5423 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5424 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5425 self
.assertNotIn('rapid-commit', output
)
5428 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5431 def test_dhcp_client_ipv6_dbus_status(self
):
5432 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5434 self
.wait_online(['veth-peer:carrier'])
5436 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5437 # bit set) has yet been received and the configuration does not include WithoutRA=true
5438 state
= get_dhcp6_client_state('veth99')
5439 print(f
"DHCPv6 client state = {state}")
5440 self
.assertEqual(state
, 'stopped')
5442 state
= get_dhcp4_client_state('veth99')
5443 print(f
"DHCPv4 client state = {state}")
5444 self
.assertEqual(state
, 'selecting')
5446 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5447 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5449 state
= get_dhcp6_client_state('veth99')
5450 print(f
"DHCPv6 client state = {state}")
5451 self
.assertEqual(state
, 'bound')
5453 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5454 for _
in range(100):
5455 state
= get_dhcp4_client_state('veth99')
5456 if state
== 'stopped':
5460 print(f
"DHCPv4 client state = {state}")
5461 self
.assertEqual(state
, 'stopped')
5463 # restart dnsmasq to clear log
5465 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5467 # Test renew command
5468 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5469 check_output(*networkctl_cmd
, 'renew', 'veth99', env
=env
)
5471 for _
in range(100):
5472 state
= get_dhcp4_client_state('veth99')
5473 if state
== 'stopped':
5477 print(f
"DHCPv4 client state = {state}")
5478 self
.assertEqual(state
, 'stopped')
5480 print('## dnsmasq log')
5481 output
= read_dnsmasq_log_file()
5483 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5484 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5485 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5486 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5488 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5489 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5492 self
.wait_online(['veth-peer:carrier'])
5494 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5497 output
= check_output('ip address show dev veth99 scope global')
5499 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5500 self
.assertNotIn('192.168.5', output
)
5502 print('## dnsmasq log')
5503 output
= read_dnsmasq_log_file()
5505 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5506 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5507 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5508 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5509 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5511 def test_dhcp_client_ipv4_only(self
):
5512 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5514 self
.setup_nftset('addr4', 'ipv4_addr')
5515 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5516 self
.setup_nftset('ifindex', 'iface_index')
5519 self
.wait_online(['veth-peer:carrier'])
5520 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5521 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5522 '--dhcp-option=option:domain-search,example.com',
5523 '--dhcp-alternate-port=67,5555',
5524 ipv4_range
='192.168.5.110,192.168.5.119')
5525 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5526 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5528 print('## ip address show dev veth99 scope global')
5529 output
= check_output('ip address show dev veth99 scope global')
5531 self
.assertIn('mtu 1492', output
)
5532 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5533 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')
5534 self
.assertNotIn('2600::', output
)
5536 output
= check_output('ip -4 --json address show dev veth99')
5537 for i
in json
.loads(output
)[0]['addr_info']:
5538 if i
['label'] == 'test-label':
5539 address1
= i
['local']
5542 self
.assertFalse(True)
5544 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5546 print('## ip route show table main dev veth99')
5547 output
= check_output('ip route show table main dev veth99')
5549 # no DHCP routes assigned to the main table
5550 self
.assertNotIn('proto dhcp', output
)
5552 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5553 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5554 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5555 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5557 print('## ip route show table 211 dev veth99')
5558 output
= check_output('ip route show table 211 dev veth99')
5560 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5561 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5562 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5563 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5564 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5565 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5567 print('## link state file')
5568 output
= read_link_state_file('veth99')
5570 # checking DNS server, SIP server, and Domains
5571 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5572 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5573 self
.assertIn('DOMAINS=example.com', output
)
5576 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5577 j
= json
.loads(output
)
5579 self
.assertEqual(len(j
['DNS']), 2)
5582 self
.assertEqual(i
['Family'], 2)
5583 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5584 self
.assertRegex(a
, '^192.168.5.[67]$')
5585 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5586 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5587 self
.assertEqual('192.168.5.1', a
)
5589 self
.assertEqual(len(j
['SIP']), 2)
5592 self
.assertEqual(i
['Family'], 2)
5593 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5594 self
.assertRegex(a
, '^192.168.5.2[12]$')
5595 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5596 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5597 self
.assertEqual('192.168.5.1', a
)
5599 print('## dnsmasq log')
5600 output
= read_dnsmasq_log_file()
5602 self
.assertIn('vendor class: FooBarVendorTest', output
)
5603 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5604 self
.assertIn('client provides name: test-hostname', output
)
5605 self
.assertIn('26:mtu', output
)
5607 # change address range, DNS servers, and Domains
5609 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5610 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5611 '--dhcp-option=option:domain-search,foo.example.com',
5612 '--dhcp-alternate-port=67,5555',
5613 ipv4_range
='192.168.5.120,192.168.5.129',)
5615 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5616 print('Wait for the DHCP lease to be expired')
5617 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5618 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5620 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5622 print('## ip address show dev veth99 scope global')
5623 output
= check_output('ip address show dev veth99 scope global')
5625 self
.assertIn('mtu 1492', output
)
5626 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5627 self
.assertNotIn(f
'{address1}', output
)
5628 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')
5629 self
.assertNotIn('2600::', output
)
5631 output
= check_output('ip -4 --json address show dev veth99')
5632 for i
in json
.loads(output
)[0]['addr_info']:
5633 if i
['label'] == 'test-label':
5634 address2
= i
['local']
5637 self
.assertFalse(True)
5639 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5641 print('## ip route show table main dev veth99')
5642 output
= check_output('ip route show table main dev veth99')
5644 # no DHCP routes assigned to the main table
5645 self
.assertNotIn('proto dhcp', output
)
5647 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5648 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5649 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5650 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5652 print('## ip route show table 211 dev veth99')
5653 output
= check_output('ip route show table 211 dev veth99')
5655 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5656 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5657 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5658 self
.assertNotIn('192.168.5.6', output
)
5659 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5660 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5661 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5663 print('## link state file')
5664 output
= read_link_state_file('veth99')
5666 # checking DNS server, SIP server, and Domains
5667 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5668 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5669 self
.assertIn('DOMAINS=foo.example.com', output
)
5672 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5673 j
= json
.loads(output
)
5675 self
.assertEqual(len(j
['DNS']), 3)
5678 self
.assertEqual(i
['Family'], 2)
5679 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5680 self
.assertRegex(a
, '^192.168.5.[178]$')
5681 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5682 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5683 self
.assertEqual('192.168.5.1', a
)
5685 self
.assertEqual(len(j
['SIP']), 2)
5688 self
.assertEqual(i
['Family'], 2)
5689 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5690 self
.assertRegex(a
, '^192.168.5.2[34]$')
5691 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5692 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5693 self
.assertEqual('192.168.5.1', a
)
5695 print('## dnsmasq log')
5696 output
= read_dnsmasq_log_file()
5698 self
.assertIn('vendor class: FooBarVendorTest', output
)
5699 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5700 self
.assertIn('client provides name: test-hostname', output
)
5701 self
.assertIn('26:mtu', output
)
5703 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5705 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5706 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5707 self
.check_nftset('ifindex', 'veth99')
5709 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5711 def test_dhcp_client_ipv4_dbus_status(self
):
5712 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5714 self
.wait_online(['veth-peer:carrier'])
5716 state
= get_dhcp4_client_state('veth99')
5717 print(f
"State = {state}")
5718 self
.assertEqual(state
, 'rebooting')
5720 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5721 '--dhcp-option=option:domain-search,example.com',
5722 '--dhcp-alternate-port=67,5555',
5723 ipv4_range
='192.168.5.110,192.168.5.119')
5724 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5725 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5727 state
= get_dhcp4_client_state('veth99')
5728 print(f
"State = {state}")
5729 self
.assertEqual(state
, 'bound')
5731 def test_dhcp_client_allow_list(self
):
5732 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5735 self
.wait_online(['veth-peer:carrier'])
5736 since
= datetime
.datetime
.now()
5739 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5741 if expect
in read_networkd_log(since
=since
):
5747 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
5748 since
= datetime
.datetime
.now()
5751 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5753 if expect
in read_networkd_log(since
=since
):
5759 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
5760 since
= datetime
.datetime
.now()
5763 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
5765 if expect
in read_networkd_log(since
=since
):
5771 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
5772 def test_dhcp_client_rapid_commit(self
):
5773 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5775 self
.wait_online(['veth-peer:carrier'])
5777 start_dnsmasq('--dhcp-rapid-commit')
5778 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5779 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5781 state
= get_dhcp4_client_state('veth99')
5782 print(f
"DHCPv4 client state = {state}")
5783 self
.assertEqual(state
, 'bound')
5785 output
= read_dnsmasq_log_file()
5786 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
5787 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
5788 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5789 self
.assertIn('DHCPACK(veth-peer)', output
)
5791 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
5792 copy_network_unit('25-veth.netdev',
5793 '25-dhcp-server-ipv6-only-mode.network',
5794 '25-dhcp-client-ipv6-only-mode.network')
5796 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
5797 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5799 state
= get_dhcp4_client_state('veth99')
5800 print(f
"State = {state}")
5801 self
.assertEqual(state
, 'bound')
5803 def test_dhcp_client_ipv4_use_routes_gateway(self
):
5805 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
5811 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
5812 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
5813 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
5815 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
5816 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
5817 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
5818 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
5819 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
5820 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
5821 copy_network_unit(*testunits
, copy_dropins
=False)
5824 self
.wait_online(['veth-peer:carrier'])
5825 additional_options
= [
5826 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
5827 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
5828 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
5831 additional_options
+= [
5832 '--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'
5834 start_dnsmasq(*additional_options
)
5835 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5837 output
= check_output('ip -4 route show dev veth99')
5843 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5844 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5845 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5846 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5847 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5849 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5850 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5851 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5852 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5854 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5855 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5856 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5857 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5858 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5859 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5860 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5861 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5864 if use_gateway
and (not classless
or not use_routes
):
5865 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5867 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5869 # Check route to gateway
5870 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
5871 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5873 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5875 # Check RoutesToDNS= and RoutesToNTP=
5876 if dns_and_ntp_routes
:
5877 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5878 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5881 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5882 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5884 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5885 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5887 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5888 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5890 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5891 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5892 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5893 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5895 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
5898 def test_dhcp_client_settings_anonymize(self
):
5899 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
5901 self
.wait_online(['veth-peer:carrier'])
5903 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5905 print('## dnsmasq log')
5906 output
= read_dnsmasq_log_file()
5908 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
5909 self
.assertNotIn('test-hostname', output
)
5910 self
.assertNotIn('26:mtu', output
)
5912 def test_dhcp_keep_configuration_dhcp(self
):
5913 copy_network_unit('25-veth.netdev',
5914 '25-dhcp-server-veth-peer.network',
5915 '25-dhcp-client-keep-configuration-dhcp.network')
5917 self
.wait_online(['veth-peer:carrier'])
5919 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5921 output
= check_output('ip address show dev veth99 scope global')
5923 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5924 'valid_lft forever preferred_lft forever')
5926 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
5929 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5930 print('Wait for the DHCP lease to be expired')
5933 # The lease address should be kept after the lease expired
5934 output
= check_output('ip address show dev veth99 scope global')
5936 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5937 'valid_lft forever preferred_lft forever')
5941 # The lease address should be kept after networkd stopped
5942 output
= check_output('ip address show dev veth99 scope global')
5944 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5945 'valid_lft forever preferred_lft forever')
5947 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
5948 f
.write('[Network]\nDHCP=no\n')
5951 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5953 # Still the lease address should be kept after networkd restarted
5954 output
= check_output('ip address show dev veth99 scope global')
5956 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5957 'valid_lft forever preferred_lft forever')
5959 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
5960 copy_network_unit('25-veth.netdev',
5961 '25-dhcp-server-veth-peer.network',
5962 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
5964 self
.wait_online(['veth-peer:carrier'])
5966 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5968 output
= check_output('ip address show dev veth99 scope global')
5970 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5975 output
= check_output('ip address show dev veth99 scope global')
5977 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5980 self
.wait_online(['veth-peer:routable'])
5982 output
= check_output('ip address show dev veth99 scope global')
5984 self
.assertNotIn('192.168.5.', output
)
5986 def test_dhcp_client_reuse_address_as_static(self
):
5987 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5989 self
.wait_online(['veth-peer:carrier'])
5991 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5993 # link become 'routable' when at least one protocol provide an valid address.
5994 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
5995 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
5997 output
= check_output('ip address show dev veth99 scope global')
5998 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
5999 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6000 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6001 print(static_network
)
6003 remove_network_unit('25-dhcp-client.network')
6005 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6006 f
.write(static_network
)
6009 self
.wait_online(['veth99:routable'])
6011 output
= check_output('ip -4 address show dev veth99 scope global')
6013 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6014 'valid_lft forever preferred_lft forever')
6016 output
= check_output('ip -6 address show dev veth99 scope global')
6018 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6019 'valid_lft forever preferred_lft forever')
6021 @expectedFailureIfModuleIsNotAvailable('vrf')
6022 def test_dhcp_client_vrf(self
):
6023 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6024 '25-vrf.netdev', '25-vrf.network')
6026 self
.wait_online(['veth-peer:carrier'])
6028 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
6030 # link become 'routable' when at least one protocol provide an valid address.
6031 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6032 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6034 print('## ip -d link show dev vrf99')
6035 output
= check_output('ip -d link show dev vrf99')
6037 self
.assertRegex(output
, 'vrf table 42')
6039 print('## ip address show vrf vrf99')
6040 output
= check_output('ip address show vrf vrf99')
6042 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6043 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6044 self
.assertRegex(output
, 'inet6 .* scope link')
6046 print('## ip address show dev veth99')
6047 output
= check_output('ip address show dev veth99')
6049 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6050 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6051 self
.assertRegex(output
, 'inet6 .* scope link')
6053 print('## ip route show vrf vrf99')
6054 output
= check_output('ip route show vrf vrf99')
6056 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6057 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6058 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6060 print('## ip route show table main dev veth99')
6061 output
= check_output('ip route show table main dev veth99')
6063 self
.assertEqual(output
, '')
6065 def test_dhcp_client_gateway_onlink_implicit(self
):
6066 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6067 '25-dhcp-client-gateway-onlink-implicit.network')
6069 self
.wait_online(['veth-peer:carrier'])
6071 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6073 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
6075 self
.assertRegex(output
, '192.168.5')
6077 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6079 self
.assertRegex(output
, 'onlink')
6080 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6082 self
.assertRegex(output
, 'onlink')
6084 def test_dhcp_client_with_ipv4ll(self
):
6085 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6086 '25-dhcp-client-with-ipv4ll.network')
6088 # we need to increase timeout above default, as this will need to wait for
6089 # systemd-networkd to get the dhcpv4 transient failure event
6090 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6092 output
= check_output('ip -4 address show dev veth99')
6094 self
.assertNotIn('192.168.5.', output
)
6095 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6098 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6099 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6100 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')
6101 self
.wait_online(['veth99:routable'])
6103 output
= check_output('ip -4 address show dev veth99')
6105 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6106 self
.assertNotIn('169.254.', output
)
6107 self
.assertNotIn('scope link', output
)
6110 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6111 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)
6112 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6114 output
= check_output('ip -4 address show dev veth99')
6116 self
.assertNotIn('192.168.5.', output
)
6117 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6119 def test_dhcp_client_use_dns(self
):
6120 def check(self
, ipv4
, ipv6
):
6121 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6122 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6123 f
.write('[DHCPv4]\nUseDNS=')
6124 f
.write('yes' if ipv4
else 'no')
6125 f
.write('\n[DHCPv6]\nUseDNS=')
6126 f
.write('yes' if ipv6
else 'no')
6127 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6130 self
.wait_online(['veth99:routable'])
6132 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6133 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6134 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6136 # make resolved re-read the link state file
6137 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
6139 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
6142 self
.assertIn('192.168.5.1', output
)
6144 self
.assertNotIn('192.168.5.1', output
)
6146 self
.assertIn('2600::1', output
)
6148 self
.assertNotIn('2600::1', output
)
6150 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6153 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6156 self
.wait_online(['veth-peer:carrier'])
6157 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6158 '--dhcp-option=option6:dns-server,[2600::1]')
6160 check(self
, True, True)
6161 check(self
, True, False)
6162 check(self
, False, True)
6163 check(self
, False, False)
6165 def test_dhcp_client_use_captive_portal(self
):
6166 def check(self
, ipv4
, ipv6
):
6167 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6168 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6169 f
.write('[DHCPv4]\nUseCaptivePortal=')
6170 f
.write('yes' if ipv4
else 'no')
6171 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6172 f
.write('yes' if ipv6
else 'no')
6173 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6176 self
.wait_online(['veth99:routable'])
6178 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6179 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6180 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6182 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6185 self
.assertIn('Captive Portal: http://systemd.io', output
)
6187 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6189 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6192 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6195 self
.wait_online(['veth-peer:carrier'])
6196 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6197 '--dhcp-option=option6:103,http://systemd.io')
6199 check(self
, True, True)
6200 check(self
, True, False)
6201 check(self
, False, True)
6202 check(self
, False, False)
6204 def test_dhcp_client_reject_captive_portal(self
):
6205 def check(self
, ipv4
, ipv6
):
6206 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6207 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6208 f
.write('[DHCPv4]\nUseCaptivePortal=')
6209 f
.write('yes' if ipv4
else 'no')
6210 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6211 f
.write('yes' if ipv6
else 'no')
6212 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6215 self
.wait_online(['veth99:routable'])
6217 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6218 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6219 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6221 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6223 self
.assertNotIn('Captive Portal: ', output
)
6224 self
.assertNotIn('invalid/url', output
)
6226 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6229 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6232 self
.wait_online(['veth-peer:carrier'])
6233 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6234 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6235 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6237 check(self
, True, True)
6238 check(self
, True, False)
6239 check(self
, False, True)
6240 check(self
, False, False)
6242 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6250 def test_dhcp6pd(self
):
6251 def get_dhcp6_prefix(link
):
6252 description
= get_link_description(link
)
6254 self
.assertIn('DHCPv6Client', description
.keys())
6255 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6257 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6261 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6262 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6263 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6264 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6265 '25-dhcp-pd-downstream-dummy97.network',
6266 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6267 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6270 self
.setup_nftset('addr6', 'ipv6_addr')
6271 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6272 self
.setup_nftset('ifindex', 'iface_index')
6275 self
.wait_online(['veth-peer:routable'])
6276 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6277 self
.wait_online(['veth99:degraded'])
6279 # First, test UseAddress=no and Assign=no (issue #29979).
6280 # Note, due to the bug #29701, this test must be done at first.
6281 print('### ip -6 address show dev veth99 scope global')
6282 output
= check_output('ip -6 address show dev veth99 scope global')
6284 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6286 # Check DBus assigned prefix information to veth99
6287 prefixInfo
= get_dhcp6_prefix('veth99')
6289 self
.assertEqual(len(prefixInfo
), 1)
6290 prefixInfo
= prefixInfo
[0]
6292 self
.assertIn('Prefix', prefixInfo
.keys())
6293 self
.assertIn('PrefixLength', prefixInfo
.keys())
6294 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6295 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6297 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6298 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6299 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6300 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6302 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6304 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6305 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6307 print('### ip -6 address show dev veth-peer scope global')
6308 output
= check_output('ip -6 address show dev veth-peer scope global')
6310 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6314 # dummy97: 0x01 (The link will appear later)
6316 # dummy99: auto -> 0x02 (No address assignment)
6321 print('### ip -6 address show dev veth99 scope global')
6322 output
= check_output('ip -6 address show dev veth99 scope global')
6325 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6326 # address in IA_PD (Token=static)
6327 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6328 # address in IA_PD (Token=eui64)
6329 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6330 # address in IA_PD (temporary)
6331 # Note that the temporary addresses may appear after the link enters configured state
6332 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')
6334 print('### ip -6 address show dev test1 scope global')
6335 output
= check_output('ip -6 address show dev test1 scope global')
6337 # address in IA_PD (Token=static)
6338 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6339 # address in IA_PD (temporary)
6340 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')
6342 print('### ip -6 address show dev dummy98 scope global')
6343 output
= check_output('ip -6 address show dev dummy98 scope global')
6345 # address in IA_PD (Token=static)
6346 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6347 # address in IA_PD (temporary)
6348 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')
6350 print('### ip -6 address show dev dummy99 scope global')
6351 output
= check_output('ip -6 address show dev dummy99 scope global')
6354 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6356 print('### ip -6 address show dev veth97 scope global')
6357 output
= check_output('ip -6 address show dev veth97 scope global')
6359 # address in IA_PD (Token=static)
6360 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6361 # address in IA_PD (Token=eui64)
6362 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6363 # address in IA_PD (temporary)
6364 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')
6366 print('### ip -6 address show dev veth97-peer scope global')
6367 output
= check_output('ip -6 address show dev veth97-peer scope global')
6369 # NDisc address (Token=static)
6370 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6371 # NDisc address (Token=eui64)
6372 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6373 # NDisc address (temporary)
6374 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')
6376 print('### ip -6 address show dev veth98 scope global')
6377 output
= check_output('ip -6 address show dev veth98 scope global')
6379 # address in IA_PD (Token=static)
6380 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6381 # address in IA_PD (Token=eui64)
6382 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6383 # address in IA_PD (temporary)
6384 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')
6386 print('### ip -6 address show dev veth98-peer scope global')
6387 output
= check_output('ip -6 address show dev veth98-peer scope global')
6389 # NDisc address (Token=static)
6390 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6391 # NDisc address (Token=eui64)
6392 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6393 # NDisc address (temporary)
6394 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')
6396 print('### ip -6 route show type unreachable')
6397 output
= check_output('ip -6 route show type unreachable')
6399 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6401 print('### ip -6 route show dev veth99')
6402 output
= check_output('ip -6 route show dev veth99')
6404 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6406 print('### ip -6 route show dev test1')
6407 output
= check_output('ip -6 route show dev test1')
6409 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6411 print('### ip -6 route show dev dummy98')
6412 output
= check_output('ip -6 route show dev dummy98')
6414 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6416 print('### ip -6 route show dev dummy99')
6417 output
= check_output('ip -6 route show dev dummy99')
6419 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6421 print('### ip -6 route show dev veth97')
6422 output
= check_output('ip -6 route show dev veth97')
6424 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6426 print('### ip -6 route show dev veth97-peer')
6427 output
= check_output('ip -6 route show dev veth97-peer')
6429 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6431 print('### ip -6 route show dev veth98')
6432 output
= check_output('ip -6 route show dev veth98')
6434 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6436 print('### ip -6 route show dev veth98-peer')
6437 output
= check_output('ip -6 route show dev veth98-peer')
6439 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6441 # Test case for a downstream which appears later
6442 check_output('ip link add dummy97 type dummy')
6443 self
.wait_online(['dummy97:routable'])
6445 print('### ip -6 address show dev dummy97 scope global')
6446 output
= check_output('ip -6 address show dev dummy97 scope global')
6448 # address in IA_PD (Token=static)
6449 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6450 # address in IA_PD (temporary)
6451 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')
6453 print('### ip -6 route show dev dummy97')
6454 output
= check_output('ip -6 route show dev dummy97')
6456 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6458 # Test case for reconfigure
6459 networkctl_reconfigure('dummy98', 'dummy99')
6460 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6462 print('### ip -6 address show dev dummy98 scope global')
6463 output
= check_output('ip -6 address show dev dummy98 scope global')
6465 # address in IA_PD (Token=static)
6466 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6467 # address in IA_PD (temporary)
6468 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')
6470 print('### ip -6 address show dev dummy99 scope global')
6471 output
= check_output('ip -6 address show dev dummy99 scope global')
6474 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6476 print('### ip -6 route show dev dummy98')
6477 output
= check_output('ip -6 route show dev dummy98')
6479 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6481 print('### ip -6 route show dev dummy99')
6482 output
= check_output('ip -6 route show dev dummy99')
6484 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6486 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6488 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6489 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6490 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6491 self
.check_nftset('ifindex', 'dummy98')
6493 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6495 def verify_dhcp4_6rd(self
, tunnel_name
):
6496 print('### ip -4 address show dev veth-peer scope global')
6497 output
= check_output('ip -4 address show dev veth-peer scope global')
6499 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6503 # dummy97: 0x01 (The link will appear later)
6505 # dummy99: auto -> 0x0[23] (No address assignment)
6506 # 6rd-XXX: auto -> 0x0[23]
6511 print('### ip -4 address show dev veth99 scope global')
6512 output
= check_output('ip -4 address show dev veth99 scope global')
6514 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6516 print('### ip -6 address show dev veth99 scope global')
6517 output
= check_output('ip -6 address show dev veth99 scope global')
6519 # address in IA_PD (Token=static)
6520 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6521 # address in IA_PD (Token=eui64)
6522 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6523 # address in IA_PD (temporary)
6524 # Note that the temporary addresses may appear after the link enters configured state
6525 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')
6527 print('### ip -6 address show dev test1 scope global')
6528 output
= check_output('ip -6 address show dev test1 scope global')
6530 # address in IA_PD (Token=static)
6531 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6532 # address in IA_PD (temporary)
6533 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')
6535 print('### ip -6 address show dev dummy98 scope global')
6536 output
= check_output('ip -6 address show dev dummy98 scope global')
6538 # address in IA_PD (Token=static)
6539 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6540 # address in IA_PD (temporary)
6541 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')
6543 print('### ip -6 address show dev dummy99 scope global')
6544 output
= check_output('ip -6 address show dev dummy99 scope global')
6547 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6549 print('### ip -6 address show dev veth97 scope global')
6550 output
= check_output('ip -6 address show dev veth97 scope global')
6552 # address in IA_PD (Token=static)
6553 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6554 # address in IA_PD (Token=eui64)
6555 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6556 # address in IA_PD (temporary)
6557 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')
6559 print('### ip -6 address show dev veth97-peer scope global')
6560 output
= check_output('ip -6 address show dev veth97-peer scope global')
6562 # NDisc address (Token=static)
6563 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6564 # NDisc address (Token=eui64)
6565 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6566 # NDisc address (temporary)
6567 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')
6569 print('### ip -6 address show dev veth98 scope global')
6570 output
= check_output('ip -6 address show dev veth98 scope global')
6572 # address in IA_PD (Token=static)
6573 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6574 # address in IA_PD (Token=eui64)
6575 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6576 # address in IA_PD (temporary)
6577 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')
6579 print('### ip -6 address show dev veth98-peer scope global')
6580 output
= check_output('ip -6 address show dev veth98-peer scope global')
6582 # NDisc address (Token=static)
6583 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6584 # NDisc address (Token=eui64)
6585 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6586 # NDisc address (temporary)
6587 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')
6589 print('### ip -6 route show type unreachable')
6590 output
= check_output('ip -6 route show type unreachable')
6592 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6594 print('### ip -6 route show dev veth99')
6595 output
= check_output('ip -6 route show dev veth99')
6597 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6599 print('### ip -6 route show dev test1')
6600 output
= check_output('ip -6 route show dev test1')
6602 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6604 print('### ip -6 route show dev dummy98')
6605 output
= check_output('ip -6 route show dev dummy98')
6607 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6609 print('### ip -6 route show dev dummy99')
6610 output
= check_output('ip -6 route show dev dummy99')
6612 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6614 print('### ip -6 route show dev veth97')
6615 output
= check_output('ip -6 route show dev veth97')
6617 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6619 print('### ip -6 route show dev veth97-peer')
6620 output
= check_output('ip -6 route show dev veth97-peer')
6622 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6624 print('### ip -6 route show dev veth98')
6625 output
= check_output('ip -6 route show dev veth98')
6627 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6629 print('### ip -6 route show dev veth98-peer')
6630 output
= check_output('ip -6 route show dev veth98-peer')
6632 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6634 print('### ip -6 address show dev dummy97 scope global')
6635 output
= check_output('ip -6 address show dev dummy97 scope global')
6637 # address in IA_PD (Token=static)
6638 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6639 # address in IA_PD (temporary)
6640 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')
6642 print('### ip -6 route show dev dummy97')
6643 output
= check_output('ip -6 route show dev dummy97')
6645 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6647 print(f
'### ip -d link show dev {tunnel_name}')
6648 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6650 self
.assertIn('link/sit 10.100.100.', output
)
6651 self
.assertIn('local 10.100.100.', output
)
6652 self
.assertIn('ttl 64', output
)
6653 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6654 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6656 print(f
'### ip -6 address show dev {tunnel_name}')
6657 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6659 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')
6660 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6662 print(f
'### ip -6 route show dev {tunnel_name}')
6663 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6665 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6666 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6668 print('### ip -6 route show default')
6669 output
= check_output('ip -6 route show default')
6671 self
.assertIn('default', output
)
6672 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6674 def test_dhcp4_6rd(self
):
6675 def get_dhcp_6rd_prefix(link
):
6676 description
= get_link_description(link
)
6678 self
.assertIn('DHCPv4Client', description
.keys())
6679 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6681 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6682 self
.assertIn('Prefix', prefixInfo
.keys())
6683 self
.assertIn('PrefixLength', prefixInfo
.keys())
6684 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6685 self
.assertIn('BorderRouters', prefixInfo
.keys())
6689 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6690 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6691 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6692 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6693 '25-dhcp-pd-downstream-dummy97.network',
6694 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6695 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6696 '80-6rd-tunnel.network')
6699 self
.wait_online(['veth-peer:routable'])
6702 # 6rd-prefix: 2001:db8::/32
6703 # br-addresss: 10.0.0.1
6705 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',
6706 ipv4_range
='10.100.100.100,10.100.100.200',
6707 ipv4_router
='10.0.0.1')
6708 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6709 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6711 # Check the DBus interface for assigned prefix information
6712 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6714 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6715 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6716 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6717 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6719 # Test case for a downstream which appears later
6720 check_output('ip link add dummy97 type dummy')
6721 self
.wait_online(['dummy97:routable'])
6725 for name
in os
.listdir('/sys/class/net/'):
6726 if name
.startswith('6rd-'):
6730 self
.wait_online([f
'{tunnel_name}:routable'])
6732 self
.verify_dhcp4_6rd(tunnel_name
)
6734 # Test case for reconfigure
6735 networkctl_reconfigure('dummy98', 'dummy99')
6736 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6738 self
.verify_dhcp4_6rd(tunnel_name
)
6740 print('Wait for the DHCP lease to be renewed/rebind')
6743 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6744 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6746 self
.verify_dhcp4_6rd(tunnel_name
)
6748 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
6756 def test_ipv6_route_prefix(self
):
6757 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
6758 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6761 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6763 output
= check_output('ip address show dev veth-peer')
6765 self
.assertIn('inet6 2001:db8:0:1:', output
)
6766 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6767 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6769 output
= check_output('ip -6 route show dev veth-peer')
6771 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6772 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6773 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
6774 self
.assertIn('2001:db0:fff::/64 via ', output
)
6775 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6776 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
6778 output
= check_output('ip address show dev veth99')
6780 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6781 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
6782 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
6783 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6785 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6787 self
.assertRegex(output
, '2001:db8:1:1::2')
6789 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6791 self
.assertIn('example.com', output
)
6793 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6796 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth-peer', env
=env
)
6800 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
6802 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
6803 self
.assertEqual(prefix
, '64:ff9b::')
6805 prefix_length
= pref64
['PrefixLength']
6806 self
.assertEqual(prefix_length
, 96)
6808 def test_ipv6_route_prefix_deny_list(self
):
6809 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
6810 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6813 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6815 output
= check_output('ip address show dev veth-peer')
6817 self
.assertIn('inet6 2001:db8:0:1:', output
)
6818 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6820 output
= check_output('ip -6 route show dev veth-peer')
6822 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6823 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6824 self
.assertIn('2001:db0:fff::/64 via ', output
)
6825 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6827 output
= check_output('ip address show dev veth99')
6829 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6830 self
.assertIn('inet6 2001:db8:0:2:', output
)
6832 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6834 self
.assertRegex(output
, '2001:db8:1:1::2')
6836 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6838 self
.assertIn('example.com', output
)
6840 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
6848 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
6854 self
.wait_online(['dummy98:routable'])
6855 self
.check_link_attr('dummy98', 'mtu', mtu
)
6856 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6858 # test normal restart
6860 self
.wait_online(['dummy98:routable'])
6861 self
.check_link_attr('dummy98', 'mtu', mtu
)
6862 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6865 self
.reset_check_mtu(mtu
, ipv6_mtu
)
6867 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
6868 ''' test setting mtu/ipv6_mtu with interface already up '''
6871 # note - changing the device mtu resets the ipv6 mtu
6872 check_output('ip link set up mtu 1501 dev dummy98')
6873 check_output('ip link set up mtu 1500 dev dummy98')
6874 self
.check_link_attr('dummy98', 'mtu', '1500')
6875 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
6877 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
6879 def test_mtu_network(self
):
6880 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
6881 self
.check_mtu('1600')
6883 def test_mtu_netdev(self
):
6884 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
6885 # note - MTU set by .netdev happens ONLY at device creation!
6886 self
.check_mtu('1600', reset
=False)
6888 def test_mtu_link(self
):
6889 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
6890 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
6891 self
.check_mtu('1600', reset
=False)
6893 def test_ipv6_mtu(self
):
6894 ''' set ipv6 mtu without setting device mtu '''
6895 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
6896 self
.check_mtu('1500', '1400')
6898 def test_ipv6_mtu_toolarge(self
):
6899 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
6900 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6901 self
.check_mtu('1500', '1500')
6903 def test_mtu_network_ipv6_mtu(self
):
6904 ''' set ipv6 mtu and set device mtu via network file '''
6905 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
6906 self
.check_mtu('1600', '1550')
6908 def test_mtu_netdev_ipv6_mtu(self
):
6909 ''' set ipv6 mtu and set device mtu via netdev file '''
6910 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6911 self
.check_mtu('1600', '1550', reset
=False)
6913 def test_mtu_link_ipv6_mtu(self
):
6914 ''' set ipv6 mtu and set device mtu via link file '''
6915 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
6916 self
.check_mtu('1600', '1550', reset
=False)
6919 if __name__
== '__main__':
6920 parser
= argparse
.ArgumentParser()
6921 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
6922 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
6923 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
6924 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
6925 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
6926 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
6927 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
6928 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
6929 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
6930 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
6931 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
6932 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
6933 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
6934 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
6935 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
6936 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
6937 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
)
6938 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
6941 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
6942 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
6943 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
6944 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
6945 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
6946 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
6947 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6948 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
6949 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
6950 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
6951 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
6952 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6953 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
6956 networkd_bin
= ns
.networkd_bin
6958 resolved_bin
= ns
.resolved_bin
6959 if ns
.timesyncd_bin
:
6960 timesyncd_bin
= ns
.timesyncd_bin
6962 udevd_bin
= ns
.udevd_bin
6963 if ns
.wait_online_bin
:
6964 wait_online_bin
= ns
.wait_online_bin
6965 if ns
.networkctl_bin
:
6966 networkctl_bin
= ns
.networkctl_bin
6967 if ns
.resolvectl_bin
:
6968 resolvectl_bin
= ns
.resolvectl_bin
6969 if ns
.timedatectl_bin
:
6970 timedatectl_bin
= ns
.timedatectl_bin
6972 udevadm_bin
= ns
.udevadm_bin
6975 systemd_source_dir
= ns
.source_dir
6977 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
6978 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
6979 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
6981 use_valgrind
= ns
.use_valgrind
6982 enable_debug
= ns
.enable_debug
6983 asan_options
= ns
.asan_options
6984 lsan_options
= ns
.lsan_options
6985 ubsan_options
= ns
.ubsan_options
6986 with_coverage
= ns
.with_coverage
6989 # Do not forget the trailing space.
6990 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
6992 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
6993 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
6994 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
6995 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
6996 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
6999 env
.update({'ASAN_OPTIONS': asan_options
})
7001 env
.update({'LSAN_OPTIONS': lsan_options
})
7003 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7005 env
.update({'SYSTEMD_MEMPOOL': '0'})
7007 wait_online_env
= env
.copy()
7009 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7011 sys
.argv
[1:] = unknown_args
7012 unittest
.main(verbosity
=3)