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')
1733 self
.wait_online(['vcan99:carrier'])
1735 @expectedFailureIfModuleIsNotAvailable('vxcan')
1736 def test_vxcan(self
):
1737 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1740 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1742 @expectedFailureIfModuleIsNotAvailable('wireguard')
1743 def test_wireguard(self
):
1744 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1745 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1746 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1747 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1749 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1751 output
= check_output('ip -4 address show dev wg99')
1753 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1755 output
= check_output('ip -4 address show dev wg99')
1757 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1759 output
= check_output('ip -6 address show dev wg99')
1761 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1763 output
= check_output('ip -4 address show dev wg98')
1765 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1767 output
= check_output('ip -6 address show dev wg98')
1769 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1771 output
= check_output('ip -4 route show dev wg99 table 1234')
1773 self
.assertIn('192.168.26.0/24 proto static metric 123', output
)
1775 output
= check_output('ip -6 route show dev wg99 table 1234')
1777 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1779 output
= check_output('ip -6 route show dev wg98 table 1234')
1781 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1782 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1783 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1784 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1785 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1786 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1787 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1788 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1789 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1790 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1791 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1792 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1793 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1794 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1795 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1796 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1797 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1798 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1799 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1800 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1801 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1802 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1803 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1804 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1805 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1806 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1807 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1808 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1809 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1810 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1811 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1812 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1813 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1814 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1815 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1816 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1817 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1818 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1819 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1820 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1821 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1822 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1823 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1824 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1825 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1826 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1828 if shutil
.which('wg'):
1831 output
= check_output('wg show wg99 listen-port')
1832 self
.assertEqual(output
, '51820')
1833 output
= check_output('wg show wg99 fwmark')
1834 self
.assertEqual(output
, '0x4d2')
1835 output
= check_output('wg show wg99 private-key')
1836 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1837 output
= check_output('wg show wg99 allowed-ips')
1838 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1839 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1840 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1841 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1842 output
= check_output('wg show wg99 persistent-keepalive')
1843 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1844 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1845 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1846 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1847 output
= check_output('wg show wg99 endpoints')
1848 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1849 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1850 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1851 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1852 output
= check_output('wg show wg99 preshared-keys')
1853 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1854 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1855 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1856 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1858 output
= check_output('wg show wg98 private-key')
1859 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1861 output
= check_output('wg show wg97 listen-port')
1862 self
.assertEqual(output
, '51821')
1863 output
= check_output('wg show wg97 fwmark')
1864 self
.assertEqual(output
, '0x4d3')
1866 def test_geneve(self
):
1867 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1870 self
.wait_online(['geneve99:degraded'])
1872 output
= check_output('ip -d link show geneve99')
1874 self
.assertRegex(output
, '192.168.22.1')
1875 self
.assertRegex(output
, '6082')
1876 self
.assertRegex(output
, 'udpcsum')
1877 self
.assertRegex(output
, 'udp6zerocsumrx')
1879 def test_ipip_tunnel(self
):
1880 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1881 '25-ipip-tunnel.netdev', '25-tunnel.network',
1882 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1883 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1884 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1886 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1888 output
= check_output('ip -d link show ipiptun99')
1890 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1891 output
= check_output('ip -d link show ipiptun98')
1893 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1894 output
= check_output('ip -d link show ipiptun97')
1896 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1897 output
= check_output('ip -d link show ipiptun96')
1899 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1901 def test_gre_tunnel(self
):
1902 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1903 '25-gre-tunnel.netdev', '25-tunnel.network',
1904 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1905 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1906 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1908 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1910 output
= check_output('ip -d link show gretun99')
1912 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1913 self
.assertRegex(output
, 'ikey 1.2.3.103')
1914 self
.assertRegex(output
, 'okey 1.2.4.103')
1915 self
.assertRegex(output
, 'iseq')
1916 self
.assertRegex(output
, 'oseq')
1917 output
= check_output('ip -d link show gretun98')
1919 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1920 self
.assertRegex(output
, 'ikey 0.0.0.104')
1921 self
.assertRegex(output
, 'okey 0.0.0.104')
1922 self
.assertNotRegex(output
, 'iseq')
1923 self
.assertNotRegex(output
, 'oseq')
1924 output
= check_output('ip -d link show gretun97')
1926 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1927 self
.assertRegex(output
, 'ikey 0.0.0.105')
1928 self
.assertRegex(output
, 'okey 0.0.0.105')
1929 self
.assertNotRegex(output
, 'iseq')
1930 self
.assertNotRegex(output
, 'oseq')
1931 output
= check_output('ip -d link show gretun96')
1933 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1934 self
.assertRegex(output
, 'ikey 0.0.0.106')
1935 self
.assertRegex(output
, 'okey 0.0.0.106')
1936 self
.assertNotRegex(output
, 'iseq')
1937 self
.assertNotRegex(output
, 'oseq')
1939 def test_ip6gre_tunnel(self
):
1940 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
1941 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1942 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1943 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1944 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1947 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1949 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
1951 output
= check_output('ip -d link show ip6gretun99')
1953 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1954 output
= check_output('ip -d link show ip6gretun98')
1956 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1957 output
= check_output('ip -d link show ip6gretun97')
1959 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1960 output
= check_output('ip -d link show ip6gretun96')
1962 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
1964 def test_gretap_tunnel(self
):
1965 copy_network_unit('12-dummy.netdev', '25-gretap.network',
1966 '25-gretap-tunnel.netdev', '25-tunnel.network',
1967 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1969 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1971 output
= check_output('ip -d link show gretap99')
1973 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1974 self
.assertRegex(output
, 'ikey 0.0.0.106')
1975 self
.assertRegex(output
, 'okey 0.0.0.106')
1976 self
.assertRegex(output
, 'iseq')
1977 self
.assertRegex(output
, 'oseq')
1978 self
.assertIn('nopmtudisc', output
)
1979 self
.assertIn('ignore-df', output
)
1980 output
= check_output('ip -d link show gretap98')
1982 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
1983 self
.assertRegex(output
, 'ikey 0.0.0.107')
1984 self
.assertRegex(output
, 'okey 0.0.0.107')
1985 self
.assertRegex(output
, 'iseq')
1986 self
.assertRegex(output
, 'oseq')
1988 def test_ip6gretap_tunnel(self
):
1989 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
1990 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
1991 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1993 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
1995 output
= check_output('ip -d link show ip6gretap99')
1997 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1998 output
= check_output('ip -d link show ip6gretap98')
2000 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2002 def test_vti_tunnel(self
):
2003 copy_network_unit('12-dummy.netdev', '25-vti.network',
2004 '25-vti-tunnel.netdev', '25-tunnel.network',
2005 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2006 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2007 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2009 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2011 output
= check_output('ip -d link show vtitun99')
2013 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2014 output
= check_output('ip -d link show vtitun98')
2016 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2017 output
= check_output('ip -d link show vtitun97')
2019 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2020 output
= check_output('ip -d link show vtitun96')
2022 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2024 def test_vti6_tunnel(self
):
2025 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2026 '25-vti6-tunnel.netdev', '25-tunnel.network',
2027 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2028 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2030 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2032 output
= check_output('ip -d link show vti6tun99')
2034 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2035 output
= check_output('ip -d link show vti6tun98')
2037 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2038 output
= check_output('ip -d link show vti6tun97')
2040 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2042 def test_ip6tnl_tunnel(self
):
2043 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2044 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2045 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2046 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2047 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2048 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2049 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2051 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2052 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2053 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2055 output
= check_output('ip -d link show ip6tnl99')
2057 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2058 output
= check_output('ip -d link show ip6tnl98')
2060 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2061 output
= check_output('ip -d link show ip6tnl97')
2063 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2064 output
= check_output('ip -d link show ip6tnl-external')
2066 self
.assertIn('ip6tnl-external@NONE:', output
)
2067 self
.assertIn('ip6tnl external ', output
)
2068 output
= check_output('ip -d link show ip6tnl-slaac')
2070 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2072 output
= check_output('ip -6 address show veth99')
2074 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2076 output
= check_output('ip -4 route show default')
2078 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2080 def test_sit_tunnel(self
):
2081 copy_network_unit('12-dummy.netdev', '25-sit.network',
2082 '25-sit-tunnel.netdev', '25-tunnel.network',
2083 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2084 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2085 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2087 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2089 output
= check_output('ip -d link show sittun99')
2091 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2092 output
= check_output('ip -d link show sittun98')
2094 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2095 output
= check_output('ip -d link show sittun97')
2097 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2098 output
= check_output('ip -d link show sittun96')
2100 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2102 def test_isatap_tunnel(self
):
2103 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2104 '25-isatap-tunnel.netdev', '25-tunnel.network')
2106 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2108 output
= check_output('ip -d link show isataptun99')
2110 self
.assertRegex(output
, "isatap ")
2112 def test_6rd_tunnel(self
):
2113 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2114 '25-6rd-tunnel.netdev', '25-tunnel.network')
2116 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2118 output
= check_output('ip -d link show sittun99')
2120 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2122 @expectedFailureIfERSPANv0IsNotSupported()
2123 def test_erspan_tunnel_v0(self
):
2124 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2125 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2126 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2128 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2130 output
= check_output('ip -d link show erspan99')
2132 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2133 self
.assertIn('erspan_ver 0', output
)
2134 self
.assertNotIn('erspan_index 123', output
)
2135 self
.assertNotIn('erspan_dir ingress', output
)
2136 self
.assertNotIn('erspan_hwid 1f', output
)
2137 self
.assertIn('ikey 0.0.0.101', output
)
2138 self
.assertIn('iseq', output
)
2139 self
.assertIn('nopmtudisc', output
)
2140 self
.assertIn('ignore-df', output
)
2141 output
= check_output('ip -d link show erspan98')
2143 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2144 self
.assertIn('erspan_ver 0', output
)
2145 self
.assertNotIn('erspan_index 124', output
)
2146 self
.assertNotIn('erspan_dir egress', output
)
2147 self
.assertNotIn('erspan_hwid 2f', output
)
2148 self
.assertIn('ikey 0.0.0.102', output
)
2149 self
.assertIn('iseq', output
)
2151 def test_erspan_tunnel_v1(self
):
2152 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2153 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2154 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2156 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2158 output
= check_output('ip -d link show erspan99')
2160 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2161 self
.assertIn('erspan_ver 1', output
)
2162 self
.assertIn('erspan_index 123', output
)
2163 self
.assertNotIn('erspan_dir ingress', output
)
2164 self
.assertNotIn('erspan_hwid 1f', output
)
2165 self
.assertIn('ikey 0.0.0.101', output
)
2166 self
.assertIn('okey 0.0.0.101', output
)
2167 self
.assertIn('iseq', output
)
2168 self
.assertIn('oseq', output
)
2169 output
= check_output('ip -d link show erspan98')
2171 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2172 self
.assertIn('erspan_ver 1', output
)
2173 self
.assertIn('erspan_index 124', output
)
2174 self
.assertNotIn('erspan_dir egress', output
)
2175 self
.assertNotIn('erspan_hwid 2f', output
)
2176 self
.assertIn('ikey 0.0.0.102', output
)
2177 self
.assertIn('okey 0.0.0.102', output
)
2178 self
.assertIn('iseq', output
)
2179 self
.assertIn('oseq', output
)
2181 @expectedFailureIfERSPANv2IsNotSupported()
2182 def test_erspan_tunnel_v2(self
):
2183 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2184 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2185 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2187 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2189 output
= check_output('ip -d link show erspan99')
2191 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2192 self
.assertIn('erspan_ver 2', output
)
2193 self
.assertNotIn('erspan_index 123', output
)
2194 self
.assertIn('erspan_dir ingress', output
)
2195 self
.assertIn('erspan_hwid 0x1f', output
)
2196 self
.assertIn('ikey 0.0.0.101', output
)
2197 self
.assertIn('okey 0.0.0.101', output
)
2198 self
.assertIn('iseq', output
)
2199 self
.assertIn('oseq', output
)
2200 output
= check_output('ip -d link show erspan98')
2202 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2203 self
.assertIn('erspan_ver 2', output
)
2204 self
.assertNotIn('erspan_index 124', output
)
2205 self
.assertIn('erspan_dir egress', output
)
2206 self
.assertIn('erspan_hwid 0x2f', output
)
2207 self
.assertIn('ikey 0.0.0.102', output
)
2208 self
.assertIn('okey 0.0.0.102', output
)
2209 self
.assertIn('iseq', output
)
2210 self
.assertIn('oseq', output
)
2212 def test_tunnel_independent(self
):
2213 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2216 self
.wait_online(['ipiptun99:carrier'])
2218 def test_tunnel_independent_loopback(self
):
2219 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2222 self
.wait_online(['ipiptun99:carrier'])
2224 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2225 def test_xfrm(self
):
2226 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2227 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2228 '26-netdev-link-local-addressing-yes.network')
2231 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2233 output
= check_output('ip -d link show dev xfrm98')
2235 self
.assertIn('xfrm98@dummy98:', output
)
2236 self
.assertIn('xfrm if_id 0x98 ', output
)
2238 output
= check_output('ip -d link show dev xfrm99')
2240 self
.assertIn('xfrm99@lo:', output
)
2241 self
.assertIn('xfrm if_id 0x99 ', output
)
2243 @expectedFailureIfModuleIsNotAvailable('fou')
2245 # The following redundant check is necessary for CentOS CI.
2246 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2247 self
.assertTrue(is_module_available('fou'))
2249 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2250 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2251 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2254 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2256 output
= check_output('ip fou show')
2258 self
.assertRegex(output
, 'port 55555 ipproto 4')
2259 self
.assertRegex(output
, 'port 55556 ipproto 47')
2261 output
= check_output('ip -d link show ipiptun96')
2263 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2264 output
= check_output('ip -d link show sittun96')
2266 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2267 output
= check_output('ip -d link show gretun96')
2269 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2270 output
= check_output('ip -d link show gretap96')
2272 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2274 def test_vxlan(self
):
2275 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2276 '25-vxlan.netdev', '25-vxlan.network',
2277 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2278 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2279 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2280 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2283 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2284 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2286 output
= check_output('ip -d -d link show vxlan99')
2288 self
.assertIn('999', output
)
2289 self
.assertIn('5555', output
)
2290 self
.assertIn('l2miss', output
)
2291 self
.assertIn('l3miss', output
)
2292 self
.assertIn('gbp', output
)
2293 # Since [0] some of the options use slightly different names and some
2294 # options with default values are shown only if the -d(etails) setting
2296 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2297 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2298 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2299 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2300 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2301 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2303 output
= check_output('bridge fdb show dev vxlan99')
2305 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2306 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2307 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2309 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'vxlan99', env
=env
)
2311 self
.assertIn('VNI: 999', output
)
2312 self
.assertIn('Destination Port: 5555', output
)
2313 self
.assertIn('Underlying Device: test1', output
)
2315 output
= check_output('bridge fdb show dev vxlan97')
2317 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2318 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2319 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2321 output
= check_output('ip -d link show vxlan-slaac')
2323 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2325 output
= check_output('ip -6 address show veth99')
2327 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2329 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2330 def test_macsec(self
):
2331 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2332 '26-macsec.network', '12-dummy.netdev')
2335 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2337 output
= check_output('ip -d link show macsec99')
2339 self
.assertRegex(output
, 'macsec99@dummy98')
2340 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2341 self
.assertRegex(output
, 'encrypt on')
2343 output
= check_output('ip macsec show macsec99')
2345 self
.assertRegex(output
, 'encrypt on')
2346 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2347 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2348 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2349 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2350 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2351 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2352 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2353 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2354 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2355 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2356 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2358 def test_nlmon(self
):
2359 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2362 self
.wait_online(['nlmon99:carrier'])
2364 @expectedFailureIfModuleIsNotAvailable('ifb')
2366 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2369 self
.wait_online(['ifb99:degraded'])
2371 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2379 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2380 def test_l2tp_udp(self
):
2381 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2382 '25-l2tp-udp.netdev', '25-l2tp.network')
2385 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2387 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2389 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2390 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2391 self
.assertRegex(output
, "Peer tunnel 11")
2392 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2393 self
.assertRegex(output
, "UDP checksum: enabled")
2395 output
= check_output('ip l2tp show session tid 10 session_id 15')
2397 self
.assertRegex(output
, "Session 15 in tunnel 10")
2398 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2399 self
.assertRegex(output
, "interface name: l2tp-ses1")
2401 output
= check_output('ip l2tp show session tid 10 session_id 17')
2403 self
.assertRegex(output
, "Session 17 in tunnel 10")
2404 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2405 self
.assertRegex(output
, "interface name: l2tp-ses2")
2407 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2408 def test_l2tp_ip(self
):
2409 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2410 '25-l2tp-ip.netdev', '25-l2tp.network')
2413 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2415 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2417 self
.assertRegex(output
, "Tunnel 10, encap IP")
2418 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2419 self
.assertRegex(output
, "Peer tunnel 12")
2421 output
= check_output('ip l2tp show session tid 10 session_id 25')
2423 self
.assertRegex(output
, "Session 25 in tunnel 10")
2424 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2425 self
.assertRegex(output
, "interface name: l2tp-ses3")
2427 output
= check_output('ip l2tp show session tid 10 session_id 27')
2429 self
.assertRegex(output
, "Session 27 in tunnel 10")
2430 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2431 self
.assertRegex(output
, "interface name: l2tp-ses4")
2433 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2441 def verify_address_static(
2471 output
= check_output('ip address show dev dummy98')
2475 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2476 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2477 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2478 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2479 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2480 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2483 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2484 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2485 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2488 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2489 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2490 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2493 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2494 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2495 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2496 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2497 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2498 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2501 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2502 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2505 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2506 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2507 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2508 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2511 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2512 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2514 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2516 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2518 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2520 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2523 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2524 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2525 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2526 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2529 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2530 prefix16
= ip4_null_16
[:-len('.0.1')]
2531 self
.assertTrue(ip4_null_24
.endswith('.1'))
2532 prefix24
= ip4_null_24
[:-len('.1')]
2533 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2534 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2535 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2536 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2539 self
.assertNotIn('10.4.4.1', output
)
2540 self
.assertNotIn('10.5.4.1', output
)
2541 self
.assertNotIn('10.5.5.1', output
)
2542 self
.assertNotIn('10.8.2.1', output
)
2543 self
.assertNotIn('10.9.3.1', output
)
2544 self
.assertNotIn('2001:db8:0:f101::2', output
)
2545 self
.assertNotIn('2001:db8:0:f103::4', output
)
2548 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2550 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2553 def test_address_static(self
):
2554 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2556 self
.setup_nftset('addr4', 'ipv4_addr')
2557 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2558 self
.setup_nftset('ifindex', 'iface_index')
2560 self
.wait_online(['dummy98:routable'])
2564 output
= check_output('ip -4 --json address show dev dummy98')
2565 for i
in json
.loads(output
)[0]['addr_info']:
2566 if i
['label'] == 'subnet16':
2567 ip4_null_16
= i
['local']
2568 elif i
['label'] == 'subnet24':
2569 ip4_null_24
= i
['local']
2570 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2571 self
.assertTrue(ip4_null_24
.endswith('.1'))
2575 output
= check_output('ip -6 --json address show dev dummy98')
2576 for i
in json
.loads(output
)[0]['addr_info']:
2577 if i
['prefixlen'] == 73:
2578 ip6_null_73
= i
['local']
2579 elif i
['prefixlen'] == 74:
2580 ip6_null_74
= i
['local']
2581 self
.assertTrue(ip6_null_73
.endswith(':1'))
2582 self
.assertTrue(ip6_null_74
.endswith(':1'))
2584 self
.verify_address_static(
2589 broadcast2
=' brd 10.4.2.255',
2590 broadcast3
=' brd 10.4.3.63',
2591 peer1
=' peer 10.5.1.101/24',
2592 peer2
=' peer 10.5.2.101/24',
2593 peer3
='/24 brd 10.5.3.255',
2594 peer4
=' peer 2001:db8:0:f103::101/128',
2595 peer5
=' peer 2001:db8:0:f103::102/128',
2600 deprecated2
=' deprecated',
2602 deprecated4
=' deprecated',
2604 flag1
=' noprefixroute',
2606 flag3
=' noprefixroute',
2607 flag4
=' home mngtmpaddr',
2608 ip4_null_16
=ip4_null_16
,
2609 ip4_null_24
=ip4_null_24
,
2610 ip6_null_73
=ip6_null_73
,
2611 ip6_null_74
=ip6_null_74
,
2614 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2615 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2616 self
.check_nftset('ifindex', 'dummy98')
2618 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2620 copy_network_unit('25-address-static.network.d/10-override.conf')
2622 self
.wait_online(['dummy98:routable'])
2623 self
.verify_address_static(
2624 label1
='new-label1',
2626 label3
='new-label3',
2627 broadcast1
=' brd 10.4.1.255',
2629 broadcast3
=' brd 10.4.3.31',
2630 peer1
=' peer 10.5.1.102/24',
2631 peer2
='/24 brd 10.5.2.255',
2632 peer3
=' peer 10.5.3.102/24',
2633 peer4
=' peer 2001:db8:0:f103::201/128',
2635 peer6
=' peer 2001:db8:0:f103::203/128',
2638 deprecated1
=' deprecated',
2640 deprecated3
=' deprecated',
2644 flag2
=' noprefixroute',
2645 flag3
=' home mngtmpaddr',
2646 flag4
=' noprefixroute',
2647 ip4_null_16
=ip4_null_16
,
2648 ip4_null_24
=ip4_null_24
,
2649 ip6_null_73
=ip6_null_73
,
2650 ip6_null_74
=ip6_null_74
,
2653 networkctl_reconfigure('dummy98')
2654 self
.wait_online(['dummy98:routable'])
2655 self
.verify_address_static(
2656 label1
='new-label1',
2658 label3
='new-label3',
2659 broadcast1
=' brd 10.4.1.255',
2661 broadcast3
=' brd 10.4.3.31',
2662 peer1
=' peer 10.5.1.102/24',
2663 peer2
='/24 brd 10.5.2.255',
2664 peer3
=' peer 10.5.3.102/24',
2665 peer4
=' peer 2001:db8:0:f103::201/128',
2667 peer6
=' peer 2001:db8:0:f103::203/128',
2670 deprecated1
=' deprecated',
2672 deprecated3
=' deprecated',
2676 flag2
=' noprefixroute',
2677 flag3
=' home mngtmpaddr',
2678 flag4
=' noprefixroute',
2679 ip4_null_16
=ip4_null_16
,
2680 ip4_null_24
=ip4_null_24
,
2681 ip6_null_73
=ip6_null_73
,
2682 ip6_null_74
=ip6_null_74
,
2686 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2687 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2688 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2689 output
= check_output('ip address show dev dummy98')
2691 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2692 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2694 # 2. reconfigure the interface, and check the deprecated flag is set again
2695 networkctl_reconfigure('dummy98')
2696 self
.wait_online(['dummy98:routable'])
2697 self
.verify_address_static(
2698 label1
='new-label1',
2700 label3
='new-label3',
2701 broadcast1
=' brd 10.4.1.255',
2703 broadcast3
=' brd 10.4.3.31',
2704 peer1
=' peer 10.5.1.102/24',
2705 peer2
='/24 brd 10.5.2.255',
2706 peer3
=' peer 10.5.3.102/24',
2707 peer4
=' peer 2001:db8:0:f103::201/128',
2709 peer6
=' peer 2001:db8:0:f103::203/128',
2712 deprecated1
=' deprecated',
2714 deprecated3
=' deprecated',
2718 flag2
=' noprefixroute',
2719 flag3
=' home mngtmpaddr',
2720 flag4
=' noprefixroute',
2721 ip4_null_16
=ip4_null_16
,
2722 ip4_null_24
=ip4_null_24
,
2723 ip6_null_73
=ip6_null_73
,
2724 ip6_null_74
=ip6_null_74
,
2727 # test for ENOBUFS issue #17012 (with reload)
2728 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2730 self
.wait_online(['dummy98:routable'])
2731 output
= check_output('ip -4 address show dev dummy98')
2732 for i
in range(1, 254):
2733 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2735 # (with reconfigure)
2736 networkctl_reconfigure('dummy98')
2737 self
.wait_online(['dummy98:routable'])
2738 output
= check_output('ip -4 address show dev dummy98')
2739 for i
in range(1, 254):
2740 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2742 def test_address_ipv4acd(self
):
2743 check_output('ip netns add ns99')
2744 check_output('ip link add veth99 type veth peer veth-peer')
2745 check_output('ip link set veth-peer netns ns99')
2746 check_output('ip link set veth99 up')
2747 check_output('ip netns exec ns99 ip link set veth-peer up')
2748 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2750 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2752 self
.wait_online(['veth99:routable'])
2754 output
= check_output('ip -4 address show dev veth99')
2756 self
.assertNotIn('192.168.100.10/24', output
)
2757 self
.assertIn('192.168.100.11/24', output
)
2759 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2761 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2763 output
= check_output('ip -4 address show dev veth99')
2765 self
.assertNotIn('192.168.100.10/24', output
)
2766 self
.assertIn('192.168.100.11/24', output
)
2768 def test_address_peer_ipv4(self
):
2769 # test for issue #17304
2770 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2772 for trial
in range(2):
2778 self
.wait_online(['dummy98:routable'])
2780 output
= check_output('ip -4 address show dev dummy98')
2781 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2783 @expectedFailureIfModuleIsNotAvailable('vrf')
2784 def test_prefix_route(self
):
2785 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2786 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2787 '25-vrf.netdev', '25-vrf.network')
2788 for trial
in range(2):
2794 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2796 output
= check_output('ip route show table 42 dev dummy98')
2797 print('### ip route show table 42 dev dummy98')
2799 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2800 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2801 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2802 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2803 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2804 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2805 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2806 output
= check_output('ip -6 route show table 42 dev dummy98')
2807 print('### ip -6 route show table 42 dev dummy98')
2811 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2812 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2813 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2814 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2815 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2816 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2817 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2818 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2822 output
= check_output('ip route show dev test1')
2823 print('### ip route show dev test1')
2825 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2826 output
= check_output('ip route show table local dev test1')
2827 print('### ip route show table local dev test1')
2829 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2830 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2831 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2832 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2833 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2834 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2835 output
= check_output('ip -6 route show dev test1')
2836 print('### ip -6 route show dev test1')
2838 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2839 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2840 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2841 output
= check_output('ip -6 route show table local dev test1')
2842 print('### ip -6 route show table local dev test1')
2844 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2845 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2846 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2847 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2848 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2850 def test_configure_without_carrier(self
):
2851 copy_network_unit('11-dummy.netdev')
2853 self
.wait_operstate('test1', 'off', '')
2854 check_output('ip link set dev test1 up carrier off')
2856 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2858 self
.wait_online(['test1:no-carrier'])
2860 carrier_map
= {'on': '1', 'off': '0'}
2861 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2862 for carrier
in ['off', 'on', 'off']:
2863 with self
.subTest(carrier
=carrier
):
2864 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2865 check_output(f
'ip link set dev test1 carrier {carrier}')
2866 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2868 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2870 self
.assertRegex(output
, '192.168.0.15')
2871 self
.assertRegex(output
, '192.168.0.1')
2872 self
.assertRegex(output
, routable_map
[carrier
])
2874 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2875 copy_network_unit('11-dummy.netdev')
2877 self
.wait_operstate('test1', 'off', '')
2878 check_output('ip link set dev test1 up carrier off')
2880 copy_network_unit('25-test1.network')
2882 self
.wait_online(['test1:no-carrier'])
2884 carrier_map
= {'on': '1', 'off': '0'}
2885 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2886 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2887 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2888 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2889 check_output(f
'ip link set dev test1 carrier {carrier}')
2890 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2892 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2895 self
.assertRegex(output
, '192.168.0.15')
2896 self
.assertRegex(output
, '192.168.0.1')
2898 self
.assertNotRegex(output
, '192.168.0.15')
2899 self
.assertNotRegex(output
, '192.168.0.1')
2900 self
.assertRegex(output
, routable_map
[carrier
])
2902 def test_routing_policy_rule(self
):
2903 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2905 self
.wait_online(['test1:degraded'])
2907 output
= check_output('ip rule list iif test1 priority 111')
2909 self
.assertRegex(output
, '111:')
2910 self
.assertRegex(output
, 'from 192.168.100.18')
2911 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2912 self
.assertRegex(output
, 'iif test1')
2913 self
.assertRegex(output
, 'oif test1')
2914 self
.assertRegex(output
, 'lookup 7')
2916 output
= check_output('ip rule list iif test1 priority 101')
2918 self
.assertRegex(output
, '101:')
2919 self
.assertRegex(output
, 'from all')
2920 self
.assertRegex(output
, 'iif test1')
2921 self
.assertRegex(output
, 'lookup 9')
2923 output
= check_output('ip -6 rule list iif test1 priority 100')
2925 self
.assertRegex(output
, '100:')
2926 self
.assertRegex(output
, 'from all')
2927 self
.assertRegex(output
, 'iif test1')
2928 self
.assertRegex(output
, 'lookup 8')
2930 output
= check_output('ip rule list iif test1 priority 102')
2932 self
.assertRegex(output
, '102:')
2933 self
.assertRegex(output
, 'from 0.0.0.0/8')
2934 self
.assertRegex(output
, 'iif test1')
2935 self
.assertRegex(output
, 'lookup 10')
2937 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2940 def test_routing_policy_rule_issue_11280(self
):
2941 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
2942 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
2944 for trial
in range(3):
2945 restart_networkd(show_logs
=(trial
> 0))
2946 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
2948 output
= check_output('ip rule list table 7')
2950 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2952 output
= check_output('ip rule list table 8')
2954 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2956 def test_routing_policy_rule_reconfigure(self
):
2957 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
2959 self
.wait_online(['test1:degraded'])
2961 output
= check_output('ip rule list table 1011')
2963 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2964 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2965 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2966 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2968 output
= check_output('ip -6 rule list table 1011')
2970 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2972 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
2974 self
.wait_online(['test1:degraded'])
2976 output
= check_output('ip rule list table 1011')
2978 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2979 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2980 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2981 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2983 output
= check_output('ip -6 rule list table 1011')
2985 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
2986 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2988 call('ip rule delete priority 10111')
2989 call('ip rule delete priority 10112')
2990 call('ip rule delete priority 10113')
2991 call('ip rule delete priority 10114')
2992 call('ip -6 rule delete priority 10113')
2994 output
= check_output('ip rule list table 1011')
2996 self
.assertEqual(output
, '')
2998 output
= check_output('ip -6 rule list table 1011')
3000 self
.assertEqual(output
, '')
3002 networkctl_reconfigure('test1')
3003 self
.wait_online(['test1:degraded'])
3005 output
= check_output('ip rule list table 1011')
3007 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3008 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3009 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3010 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3012 output
= check_output('ip -6 rule list table 1011')
3014 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3016 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3017 def test_routing_policy_rule_port_range(self
):
3018 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3020 self
.wait_online(['test1:degraded'])
3022 output
= check_output('ip rule')
3024 self
.assertRegex(output
, '111')
3025 self
.assertRegex(output
, 'from 192.168.100.18')
3026 self
.assertRegex(output
, '1123-1150')
3027 self
.assertRegex(output
, '3224-3290')
3028 self
.assertRegex(output
, 'tcp')
3029 self
.assertRegex(output
, 'lookup 7')
3031 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3032 def test_routing_policy_rule_invert(self
):
3033 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3035 self
.wait_online(['test1:degraded'])
3037 output
= check_output('ip rule')
3039 self
.assertRegex(output
, '111')
3040 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3041 self
.assertRegex(output
, 'tcp')
3042 self
.assertRegex(output
, 'lookup 7')
3044 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3045 def test_routing_policy_rule_uidrange(self
):
3046 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3048 self
.wait_online(['test1:degraded'])
3050 output
= check_output('ip rule')
3052 self
.assertRegex(output
, '111')
3053 self
.assertRegex(output
, 'from 192.168.100.18')
3054 self
.assertRegex(output
, 'lookup 7')
3055 self
.assertRegex(output
, 'uidrange 100-200')
3057 def _test_route_static(self
, manage_foreign_routes
):
3058 if not manage_foreign_routes
:
3059 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3061 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3062 '25-route-static-test1.network', '11-dummy.netdev')
3064 self
.wait_online(['dummy98:routable'])
3066 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3069 print('### ip -6 route show dev dummy98')
3070 output
= check_output('ip -6 route show dev dummy98')
3072 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3073 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3074 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3076 print('### ip -6 route show default')
3077 output
= check_output('ip -6 route show default')
3079 self
.assertIn('default', output
)
3080 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3082 print('### ip -4 route show dev dummy98')
3083 output
= check_output('ip -4 route show dev dummy98')
3085 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3086 self
.assertIn('149.10.124.64 proto static scope link', output
)
3087 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3088 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3089 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3090 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3091 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3092 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3094 print('### ip -4 route show dev dummy98 default')
3095 output
= check_output('ip -4 route show dev dummy98 default')
3097 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3098 self
.assertIn('default via 149.10.124.64 proto static', output
)
3099 self
.assertIn('default proto static', output
)
3100 self
.assertIn('default via 1.1.8.104 proto static', output
)
3102 print('### ip -4 route show table local dev dummy98')
3103 output
= check_output('ip -4 route show table local dev dummy98')
3105 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3106 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3107 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3109 print('### ip -4 route show type blackhole')
3110 output
= check_output('ip -4 route show type blackhole')
3112 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3114 print('### ip -4 route show type unreachable')
3115 output
= check_output('ip -4 route show type unreachable')
3117 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3119 print('### ip -4 route show type prohibit')
3120 output
= check_output('ip -4 route show type prohibit')
3122 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3124 print('### ip -6 route show type blackhole')
3125 output
= check_output('ip -6 route show type blackhole')
3127 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3129 print('### ip -6 route show type unreachable')
3130 output
= check_output('ip -6 route show type unreachable')
3132 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3134 print('### ip -6 route show type prohibit')
3135 output
= check_output('ip -6 route show type prohibit')
3137 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3139 print('### ip route show 192.168.10.1')
3140 output
= check_output('ip route show 192.168.10.1')
3142 self
.assertIn('192.168.10.1 proto static', output
)
3143 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3144 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3145 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3146 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3148 print('### ip route show 192.168.10.2')
3149 output
= check_output('ip route show 192.168.10.2')
3151 # old ip command does not show IPv6 gateways...
3152 self
.assertIn('192.168.10.2 proto static', output
)
3153 self
.assertIn('nexthop', output
)
3154 self
.assertIn('dev test1 weight 20', output
)
3155 self
.assertIn('dev test1 weight 30', output
)
3156 self
.assertIn('dev dummy98 weight 10', output
)
3157 self
.assertIn('dev dummy98 weight 5', output
)
3159 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3160 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3162 # old ip command does not show 'nexthop' keyword and weight...
3163 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3164 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3165 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3166 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3167 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3169 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3172 copy_network_unit('25-address-static.network')
3174 self
.wait_online(['dummy98:routable'])
3176 # check all routes managed by Manager are removed
3177 print('### ip -4 route show type blackhole')
3178 output
= check_output('ip -4 route show type blackhole')
3180 self
.assertEqual(output
, '')
3182 print('### ip -4 route show type unreachable')
3183 output
= check_output('ip -4 route show type unreachable')
3185 self
.assertEqual(output
, '')
3187 print('### ip -4 route show type prohibit')
3188 output
= check_output('ip -4 route show type prohibit')
3190 self
.assertEqual(output
, '')
3192 print('### ip -6 route show type blackhole')
3193 output
= check_output('ip -6 route show type blackhole')
3195 self
.assertEqual(output
, '')
3197 print('### ip -6 route show type unreachable')
3198 output
= check_output('ip -6 route show type unreachable')
3200 self
.assertEqual(output
, '')
3202 print('### ip -6 route show type prohibit')
3203 output
= check_output('ip -6 route show type prohibit')
3205 self
.assertEqual(output
, '')
3207 remove_network_unit('25-address-static.network')
3209 self
.wait_online(['dummy98:routable'])
3211 # check all routes managed by Manager are reconfigured
3212 print('### ip -4 route show type blackhole')
3213 output
= check_output('ip -4 route show type blackhole')
3215 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3217 print('### ip -4 route show type unreachable')
3218 output
= check_output('ip -4 route show type unreachable')
3220 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3222 print('### ip -4 route show type prohibit')
3223 output
= check_output('ip -4 route show type prohibit')
3225 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3227 print('### ip -6 route show type blackhole')
3228 output
= check_output('ip -6 route show type blackhole')
3230 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3232 print('### ip -6 route show type unreachable')
3233 output
= check_output('ip -6 route show type unreachable')
3235 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3237 print('### ip -6 route show type prohibit')
3238 output
= check_output('ip -6 route show type prohibit')
3240 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3242 remove_link('dummy98')
3245 # check all routes managed by Manager are removed
3246 print('### ip -4 route show type blackhole')
3247 output
= check_output('ip -4 route show type blackhole')
3249 self
.assertEqual(output
, '')
3251 print('### ip -4 route show type unreachable')
3252 output
= check_output('ip -4 route show type unreachable')
3254 self
.assertEqual(output
, '')
3256 print('### ip -4 route show type prohibit')
3257 output
= check_output('ip -4 route show type prohibit')
3259 self
.assertEqual(output
, '')
3261 print('### ip -6 route show type blackhole')
3262 output
= check_output('ip -6 route show type blackhole')
3264 self
.assertEqual(output
, '')
3266 print('### ip -6 route show type unreachable')
3267 output
= check_output('ip -6 route show type unreachable')
3269 self
.assertEqual(output
, '')
3271 print('### ip -6 route show type prohibit')
3272 output
= check_output('ip -6 route show type prohibit')
3274 self
.assertEqual(output
, '')
3278 def test_route_static(self
):
3280 for manage_foreign_routes
in [True, False]:
3286 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3287 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3288 self
._test
_route
_static
(manage_foreign_routes
)
3290 @expectedFailureIfRTA_VIAIsNotSupported()
3291 def test_route_via_ipv6(self
):
3292 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3294 self
.wait_online(['dummy98:routable'])
3296 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3299 print('### ip -6 route show dev dummy98')
3300 output
= check_output('ip -6 route show dev dummy98')
3302 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3303 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3305 print('### ip -4 route show dev dummy98')
3306 output
= check_output('ip -4 route show dev dummy98')
3308 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3309 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3311 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3312 def test_route_congctl(self
):
3313 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3315 self
.wait_online(['dummy98:routable'])
3317 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3318 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3320 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3321 self
.assertIn('congctl dctcp', output
)
3323 print('### ip -4 route show dev dummy98 149.10.124.66')
3324 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3326 self
.assertIn('149.10.124.66 proto static', output
)
3327 self
.assertIn('congctl dctcp', output
)
3328 self
.assertIn('rto_min 300s', output
)
3330 @expectedFailureIfModuleIsNotAvailable('vrf')
3331 def test_route_vrf(self
):
3332 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3333 '25-vrf.netdev', '25-vrf.network')
3335 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3337 output
= check_output('ip route show vrf vrf99')
3339 self
.assertRegex(output
, 'default via 192.168.100.1')
3341 output
= check_output('ip route show')
3343 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3345 def test_gateway_reconfigure(self
):
3346 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3348 self
.wait_online(['dummy98:routable'])
3349 print('### ip -4 route show dev dummy98 default')
3350 output
= check_output('ip -4 route show dev dummy98 default')
3352 self
.assertIn('default via 149.10.124.59 proto static', output
)
3353 self
.assertNotIn('149.10.124.60', output
)
3355 remove_network_unit('25-gateway-static.network')
3356 copy_network_unit('25-gateway-next-static.network')
3358 self
.wait_online(['dummy98:routable'])
3359 print('### ip -4 route show dev dummy98 default')
3360 output
= check_output('ip -4 route show dev dummy98 default')
3362 self
.assertNotIn('149.10.124.59', output
)
3363 self
.assertIn('default via 149.10.124.60 proto static', output
)
3365 def test_ip_route_ipv6_src_route(self
):
3366 # a dummy device does not make the addresses go through tentative state, so we
3367 # reuse a bond from an earlier test, which does make the addresses go through
3368 # tentative state, and do our test on that
3369 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3371 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3373 output
= check_output('ip -6 route list dev bond199')
3375 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3377 def test_route_preferred_source_with_existing_address(self
):
3379 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3384 networkctl_reconfigure('dummy98')
3386 self
.wait_online(['dummy98:routable'])
3388 output
= check_output('ip -6 route list dev dummy98')
3390 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3392 def test_ip_link_mac_address(self
):
3393 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3395 self
.wait_online(['dummy98:degraded'])
3397 output
= check_output('ip link show dummy98')
3399 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3401 def test_ip_link_unmanaged(self
):
3402 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3405 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3407 def test_ipv6_address_label(self
):
3408 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3410 self
.wait_online(['dummy98:degraded'])
3412 output
= check_output('ip addrlabel list')
3414 self
.assertRegex(output
, '2004:da8:1::/64')
3416 def test_ipv6_proxy_ndp(self
):
3417 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3420 self
.wait_online(['dummy98:routable'])
3422 output
= check_output('ip neighbor show proxy dev dummy98')
3424 for i
in range(1, 5):
3425 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3427 def test_neighbor_section(self
):
3428 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3430 self
.wait_online(['dummy98:degraded'])
3432 print('### ip neigh list dev dummy98')
3433 output
= check_output('ip neigh list dev dummy98')
3435 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3436 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3437 self
.assertNotIn('2004:da8:1:0::2', output
)
3438 self
.assertNotIn('192.168.10.2', output
)
3439 self
.assertNotIn('00:00:5e:00:02:67', output
)
3441 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3444 copy_network_unit('25-neighbor-section.network.d/override.conf')
3446 self
.wait_online(['dummy98:degraded'])
3448 print('### ip neigh list dev dummy98 (after reloading)')
3449 output
= check_output('ip neigh list dev dummy98')
3451 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3452 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3453 self
.assertNotIn('2004:da8:1:0::2', output
)
3454 self
.assertNotIn('192.168.10.2', output
)
3455 self
.assertNotIn('00:00:5e:00:02', output
)
3457 def test_neighbor_reconfigure(self
):
3458 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3460 self
.wait_online(['dummy98:degraded'])
3462 print('### ip neigh list dev dummy98')
3463 output
= check_output('ip neigh list dev dummy98')
3465 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3466 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3468 remove_network_unit('25-neighbor-section.network')
3469 copy_network_unit('25-neighbor-next.network')
3471 self
.wait_online(['dummy98:degraded'])
3472 print('### ip neigh list dev dummy98')
3473 output
= check_output('ip neigh list dev dummy98')
3475 self
.assertNotIn('00:00:5e:00:02:65', output
)
3476 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3477 self
.assertNotIn('2004:da8:1::1', output
)
3479 def test_neighbor_gre(self
):
3480 copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
3481 '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
3483 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout
='40s')
3485 output
= check_output('ip neigh list dev gretun97')
3487 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3488 self
.assertNotIn('10.0.0.23', output
)
3490 output
= check_output('ip neigh list dev ip6gretun97')
3492 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3493 self
.assertNotIn('2001:db8:0:f102::18', output
)
3495 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3498 def test_link_local_addressing(self
):
3499 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3500 '25-link-local-addressing-no.network', '12-dummy.netdev')
3502 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3504 output
= check_output('ip address show dev test1')
3506 self
.assertRegex(output
, 'inet .* scope link')
3507 self
.assertRegex(output
, 'inet6 .* scope link')
3509 output
= check_output('ip address show dev dummy98')
3511 self
.assertNotRegex(output
, 'inet6* .* scope link')
3513 # Documentation/networking/ip-sysctl.txt
3515 # addr_gen_mode - INTEGER
3516 # Defines how link-local and autoconf addresses are generated.
3518 # 0: generate address based on EUI64 (default)
3519 # 1: do no generate a link-local address, use EUI64 for addresses generated
3521 # 2: generate stable privacy addresses, using the secret from
3522 # stable_secret (RFC7217)
3523 # 3: generate stable privacy addresses, using a random secret if unset
3525 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3526 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3527 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3529 def test_link_local_addressing_ipv6ll(self
):
3530 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3532 self
.wait_online(['dummy98:degraded'])
3534 # An IPv6LL address exists by default.
3535 output
= check_output('ip address show dev dummy98')
3537 self
.assertRegex(output
, 'inet6 .* scope link')
3539 copy_network_unit('25-link-local-addressing-no.network')
3541 self
.wait_online(['dummy98:carrier'])
3543 # Check if the IPv6LL address is removed.
3544 output
= check_output('ip address show dev dummy98')
3546 self
.assertNotRegex(output
, 'inet6 .* scope link')
3548 remove_network_unit('25-link-local-addressing-no.network')
3550 self
.wait_online(['dummy98:degraded'])
3552 # Check if a new IPv6LL address is assigned.
3553 output
= check_output('ip address show dev dummy98')
3555 self
.assertRegex(output
, 'inet6 .* scope link')
3557 def test_sysctl(self
):
3558 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3559 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3561 self
.wait_online(['dummy98:degraded'])
3563 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3564 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3565 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3566 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3567 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3568 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3569 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3570 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3571 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3573 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3575 self
.wait_online(['dummy98:degraded'])
3577 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3579 def test_sysctl_disable_ipv6(self
):
3580 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3582 print('## Disable ipv6')
3583 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3584 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3587 self
.wait_online(['dummy98:routable'])
3589 output
= check_output('ip -4 address show dummy98')
3591 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3592 output
= check_output('ip -6 address show dummy98')
3594 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3595 self
.assertRegex(output
, 'inet6 .* scope link')
3596 output
= check_output('ip -4 route show dev dummy98')
3598 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3599 output
= check_output('ip -6 route show default')
3601 self
.assertRegex(output
, 'default')
3602 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3604 remove_link('dummy98')
3606 print('## Enable ipv6')
3607 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3608 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3611 self
.wait_online(['dummy98:routable'])
3613 output
= check_output('ip -4 address show dummy98')
3615 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3616 output
= check_output('ip -6 address show dummy98')
3618 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3619 self
.assertRegex(output
, 'inet6 .* scope link')
3620 output
= check_output('ip -4 route show dev dummy98')
3622 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3623 output
= check_output('ip -6 route show default')
3625 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3627 def test_bind_carrier(self
):
3628 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3631 # no bound interface.
3632 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3633 output
= check_output('ip address show test1')
3635 self
.assertNotIn('UP,LOWER_UP', output
)
3636 self
.assertIn('DOWN', output
)
3637 self
.assertNotIn('192.168.10', output
)
3639 # add one bound interface. The interface will be up.
3640 check_output('ip link add dummy98 type dummy')
3641 check_output('ip link set dummy98 up')
3642 self
.wait_online(['test1:routable'])
3643 output
= check_output('ip address show test1')
3645 self
.assertIn('UP,LOWER_UP', output
)
3646 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3648 # add another bound interface. The interface is still up.
3649 check_output('ip link add dummy99 type dummy')
3650 check_output('ip link set dummy99 up')
3651 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3652 output
= check_output('ip address show test1')
3654 self
.assertIn('UP,LOWER_UP', output
)
3655 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3657 # remove one of the bound interfaces. The interface is still up
3658 remove_link('dummy98')
3659 output
= check_output('ip address show test1')
3661 self
.assertIn('UP,LOWER_UP', output
)
3662 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3664 # bring down the remaining bound interface. The interface will be down.
3665 check_output('ip link set dummy99 down')
3666 self
.wait_operstate('test1', 'off')
3667 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3668 output
= check_output('ip address show test1')
3670 self
.assertNotIn('UP,LOWER_UP', output
)
3671 self
.assertIn('DOWN', output
)
3672 self
.assertNotIn('192.168.10', output
)
3674 # bring up the bound interface. The interface will be up.
3675 check_output('ip link set dummy99 up')
3676 self
.wait_online(['test1:routable'])
3677 output
= check_output('ip address show test1')
3679 self
.assertIn('UP,LOWER_UP', output
)
3680 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3682 # remove the remaining bound interface. The interface will be down.
3683 remove_link('dummy99')
3684 self
.wait_operstate('test1', 'off')
3685 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3686 output
= check_output('ip address show test1')
3688 self
.assertNotIn('UP,LOWER_UP', output
)
3689 self
.assertIn('DOWN', output
)
3690 self
.assertNotIn('192.168.10', output
)
3692 # re-add one bound interface. The interface will be up.
3693 check_output('ip link add dummy98 type dummy')
3694 check_output('ip link set dummy98 up')
3695 self
.wait_online(['test1:routable'])
3696 output
= check_output('ip address show test1')
3698 self
.assertIn('UP,LOWER_UP', output
)
3699 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3701 def _test_activation_policy(self
, interface
, test
):
3702 conffile
= '25-activation-policy.network'
3704 conffile
= f
'{conffile}.d/{test}.conf'
3705 if interface
== 'vlan99':
3706 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3707 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3710 always
= test
.startswith('always')
3711 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3712 expect_up
= initial_up
3713 next_up
= not expect_up
3715 if test
.endswith('down'):
3716 self
.wait_activated(interface
)
3718 for iteration
in range(4):
3719 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3720 operstate
= 'routable' if expect_up
else 'off'
3721 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3722 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3725 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3726 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3727 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3729 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3732 check_output(f
'ip link set dev {interface} up')
3734 check_output(f
'ip link set dev {interface} down')
3735 expect_up
= initial_up
if always
else next_up
3736 next_up
= not next_up
3740 def test_activation_policy(self
):
3742 for interface
in ['test1', 'vlan99']:
3743 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3749 print(f
'### test_activation_policy(interface={interface}, test={test})')
3750 with self
.subTest(interface
=interface
, test
=test
):
3751 self
._test
_activation
_policy
(interface
, test
)
3753 def _test_activation_policy_required_for_online(self
, policy
, required
):
3754 conffile
= '25-activation-policy.network'
3755 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3757 units
+= [f
'{conffile}.d/{policy}.conf']
3759 units
+= [f
'{conffile}.d/required-{required}.conf']
3760 copy_network_unit(*units
, copy_dropins
=False)
3763 if policy
.endswith('down'):
3764 self
.wait_activated('test1')
3766 if policy
.endswith('down') or policy
== 'manual':
3767 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3769 self
.wait_online(['test1'])
3771 if policy
== 'always-down':
3772 # if always-down, required for online is forced to no
3775 # otherwise if required for online is specified, it should match that
3776 expected
= required
== 'yes'
3778 # otherwise if only policy specified, required for online defaults to
3779 # true if policy is up, always-up, or bound
3780 expected
= policy
.endswith('up') or policy
== 'bound'
3782 # default is true, if neither are specified
3785 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
3788 yesno
= 'yes' if expected
else 'no'
3789 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3791 def test_activation_policy_required_for_online(self
):
3793 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3794 for required
in ['yes', 'no', '']:
3800 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3801 with self
.subTest(policy
=policy
, required
=required
):
3802 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3804 def test_domain(self
):
3805 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3807 self
.wait_online(['dummy98:routable'])
3809 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3811 self
.assertRegex(output
, 'Address: 192.168.42.100')
3812 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3813 self
.assertRegex(output
, 'Search Domains: one')
3815 def test_keep_configuration_static(self
):
3816 check_output('ip link add name dummy98 type dummy')
3817 check_output('ip address add 10.1.2.3/16 dev dummy98')
3818 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3819 output
= check_output('ip address show dummy98')
3821 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3822 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3823 output
= check_output('ip route show dev dummy98')
3826 copy_network_unit('24-keep-configuration-static.network')
3828 self
.wait_online(['dummy98:routable'])
3830 output
= check_output('ip address show dummy98')
3832 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3833 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3835 @expectedFailureIfNexthopIsNotAvailable()
3836 def test_nexthop(self
):
3837 def check_nexthop(self
):
3838 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3840 output
= check_output('ip nexthop list dev veth99')
3842 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3843 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3844 self
.assertIn('id 3 dev veth99', output
)
3845 self
.assertIn('id 4 dev veth99', output
)
3846 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3847 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3848 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3850 output
= check_output('ip nexthop list dev dummy98')
3852 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
3854 # kernel manages blackhole nexthops on lo
3855 output
= check_output('ip nexthop list dev lo')
3857 self
.assertIn('id 6 blackhole', output
)
3858 self
.assertIn('id 7 blackhole', output
)
3860 # group nexthops are shown with -0 option
3861 output
= check_output('ip -0 nexthop list id 21')
3863 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
3865 output
= check_output('ip route show dev veth99 10.10.10.10')
3867 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
3869 output
= check_output('ip route show dev veth99 10.10.10.11')
3871 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
3873 output
= check_output('ip route show dev veth99 10.10.10.12')
3875 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
3877 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3879 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3881 output
= check_output('ip route show 10.10.10.13')
3883 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
3885 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
3887 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
3889 output
= check_output('ip route show 10.10.10.14')
3891 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
3892 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
3893 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
3895 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3898 copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
3899 '12-dummy.netdev', '25-nexthop-dummy.network')
3904 remove_network_unit('25-nexthop.network')
3905 copy_network_unit('25-nexthop-nothing.network')
3907 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3909 output
= check_output('ip nexthop list dev veth99')
3911 self
.assertEqual(output
, '')
3912 output
= check_output('ip nexthop list dev lo')
3914 self
.assertEqual(output
, '')
3916 remove_network_unit('25-nexthop-nothing.network')
3917 copy_network_unit('25-nexthop.network')
3918 networkctl_reconfigure('dummy98')
3923 remove_link('veth99')
3926 output
= check_output('ip nexthop list dev lo')
3928 self
.assertEqual(output
, '')
3930 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
3938 @expectedFailureIfModuleIsNotAvailable('sch_cake')
3939 def test_qdisc_cake(self
):
3940 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
3942 self
.wait_online(['dummy98:routable'])
3944 output
= check_output('tc qdisc show dev dummy98')
3946 self
.assertIn('qdisc cake 3a: root', output
)
3947 self
.assertIn('bandwidth 500Mbit', output
)
3948 self
.assertIn('autorate-ingress', output
)
3949 self
.assertIn('diffserv8', output
)
3950 self
.assertIn('dual-dsthost', output
)
3951 self
.assertIn(' nat', output
)
3952 self
.assertIn(' wash', output
)
3953 self
.assertIn(' split-gso', output
)
3954 self
.assertIn(' raw', output
)
3955 self
.assertIn(' atm', output
)
3956 self
.assertIn('overhead 128', output
)
3957 self
.assertIn('mpu 20', output
)
3958 self
.assertIn('fwmark 0xff00', output
)
3959 self
.assertIn('rtt 1s', output
)
3960 self
.assertIn('ack-filter-aggressive', output
)
3962 @expectedFailureIfModuleIsNotAvailable('sch_codel')
3963 def test_qdisc_codel(self
):
3964 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
3966 self
.wait_online(['dummy98:routable'])
3968 output
= check_output('tc qdisc show dev dummy98')
3970 self
.assertRegex(output
, 'qdisc codel 33: root')
3971 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
3973 @expectedFailureIfModuleIsNotAvailable('sch_drr')
3974 def test_qdisc_drr(self
):
3975 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
3977 self
.wait_online(['dummy98:routable'])
3979 output
= check_output('tc qdisc show dev dummy98')
3981 self
.assertRegex(output
, 'qdisc drr 2: root')
3982 output
= check_output('tc class show dev dummy98')
3984 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
3986 @expectedFailureIfModuleIsNotAvailable('sch_ets')
3987 def test_qdisc_ets(self
):
3988 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
3990 self
.wait_online(['dummy98:routable'])
3992 output
= check_output('tc qdisc show dev dummy98')
3995 self
.assertRegex(output
, 'qdisc ets 3a: root')
3996 self
.assertRegex(output
, 'bands 10 strict 3')
3997 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
3998 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4000 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4001 def test_qdisc_fq(self
):
4002 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4004 self
.wait_online(['dummy98:routable'])
4006 output
= check_output('tc qdisc show dev dummy98')
4008 self
.assertRegex(output
, 'qdisc fq 32: root')
4009 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4010 self
.assertRegex(output
, 'quantum 1500')
4011 self
.assertRegex(output
, 'initial_quantum 13000')
4012 self
.assertRegex(output
, 'maxrate 1Mbit')
4014 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4015 def test_qdisc_fq_codel(self
):
4016 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4018 self
.wait_online(['dummy98:routable'])
4020 output
= check_output('tc qdisc show dev dummy98')
4022 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4023 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')
4025 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4026 def test_qdisc_fq_pie(self
):
4027 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4029 self
.wait_online(['dummy98:routable'])
4031 output
= check_output('tc qdisc show dev dummy98')
4034 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4035 self
.assertRegex(output
, 'limit 200000p')
4037 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4038 def test_qdisc_gred(self
):
4039 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4041 self
.wait_online(['dummy98:routable'])
4043 output
= check_output('tc qdisc show dev dummy98')
4045 self
.assertRegex(output
, 'qdisc gred 38: root')
4046 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4048 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4049 def test_qdisc_hhf(self
):
4050 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4052 self
.wait_online(['dummy98:routable'])
4054 output
= check_output('tc qdisc show dev dummy98')
4056 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4057 self
.assertRegex(output
, 'limit 1022p')
4059 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4060 def test_qdisc_htb_fifo(self
):
4061 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4063 self
.wait_online(['dummy98:routable'])
4065 output
= check_output('tc qdisc show dev dummy98')
4067 self
.assertRegex(output
, 'qdisc htb 2: root')
4068 self
.assertRegex(output
, r
'default (0x30|30)')
4070 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4071 self
.assertRegex(output
, 'limit 100000p')
4073 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4074 self
.assertRegex(output
, 'limit 1000000')
4076 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4077 self
.assertRegex(output
, 'limit 1023p')
4079 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4081 output
= check_output('tc -d class show dev dummy98')
4083 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4084 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4085 # which is fixed in v6.3.0 by
4086 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4087 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4088 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4089 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4090 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4091 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4092 self
.assertRegex(output
, 'burst 123456')
4093 self
.assertRegex(output
, 'cburst 123457')
4095 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4096 def test_qdisc_ingress(self
):
4097 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4098 '25-qdisc-ingress.network', '11-dummy.netdev')
4100 self
.wait_online(['dummy98:routable', 'test1:routable'])
4102 output
= check_output('tc qdisc show dev dummy98')
4104 self
.assertRegex(output
, 'qdisc clsact')
4106 output
= check_output('tc qdisc show dev test1')
4108 self
.assertRegex(output
, 'qdisc ingress')
4110 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4111 def test_qdisc_netem(self
):
4112 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4113 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4115 self
.wait_online(['dummy98:routable', 'test1:routable'])
4117 output
= check_output('tc qdisc show dev dummy98')
4119 self
.assertRegex(output
, 'qdisc netem 30: root')
4120 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4122 output
= check_output('tc qdisc show dev test1')
4124 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4125 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4127 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4128 def test_qdisc_pie(self
):
4129 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4131 self
.wait_online(['dummy98:routable'])
4133 output
= check_output('tc qdisc show dev dummy98')
4135 self
.assertRegex(output
, 'qdisc pie 3a: root')
4136 self
.assertRegex(output
, 'limit 200000')
4138 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4139 def test_qdisc_qfq(self
):
4140 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4142 self
.wait_online(['dummy98:routable'])
4144 output
= check_output('tc qdisc show dev dummy98')
4146 self
.assertRegex(output
, 'qdisc qfq 2: root')
4147 output
= check_output('tc class show dev dummy98')
4149 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4150 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4152 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4153 def test_qdisc_sfb(self
):
4154 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4156 self
.wait_online(['dummy98:routable'])
4158 output
= check_output('tc qdisc show dev dummy98')
4160 self
.assertRegex(output
, 'qdisc sfb 39: root')
4161 self
.assertRegex(output
, 'limit 200000')
4163 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4164 def test_qdisc_sfq(self
):
4165 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4167 self
.wait_online(['dummy98:routable'])
4169 output
= check_output('tc qdisc show dev dummy98')
4171 self
.assertRegex(output
, 'qdisc sfq 36: root')
4172 self
.assertRegex(output
, 'perturb 5sec')
4174 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4175 def test_qdisc_tbf(self
):
4176 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4178 self
.wait_online(['dummy98:routable'])
4180 output
= check_output('tc qdisc show dev dummy98')
4182 self
.assertRegex(output
, 'qdisc tbf 35: root')
4183 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4185 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4186 def test_qdisc_teql(self
):
4187 call_quiet('rmmod sch_teql')
4189 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4191 self
.wait_links('dummy98')
4192 check_output('modprobe sch_teql max_equalizers=2')
4193 self
.wait_online(['dummy98:routable'])
4195 output
= check_output('tc qdisc show dev dummy98')
4197 self
.assertRegex(output
, 'qdisc teql1 31: root')
4199 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4207 def test_state_file(self
):
4208 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4210 self
.wait_online(['dummy98:routable'])
4212 # make link state file updated
4213 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4215 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4218 output
= read_link_state_file('dummy98')
4220 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4221 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4222 self
.assertIn('ADMIN_STATE=configured', output
)
4223 self
.assertIn('OPER_STATE=routable', output
)
4224 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4225 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4226 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4227 self
.assertIn('ACTIVATION_POLICY=up', output
)
4228 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4229 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4230 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4231 self
.assertIn('DOMAINS=hogehoge', output
)
4232 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4233 self
.assertIn('LLMNR=no', output
)
4234 self
.assertIn('MDNS=yes', output
)
4235 self
.assertIn('DNSSEC=no', output
)
4237 check_output(*resolvectl_cmd
, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env
=env
)
4238 check_output(*resolvectl_cmd
, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env
=env
)
4239 check_output(*resolvectl_cmd
, 'llmnr', 'dummy98', 'yes', env
=env
)
4240 check_output(*resolvectl_cmd
, 'mdns', 'dummy98', 'no', env
=env
)
4241 check_output(*resolvectl_cmd
, 'dnssec', 'dummy98', 'yes', env
=env
)
4242 check_output(*timedatectl_cmd
, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env
=env
)
4244 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4247 output
= read_link_state_file('dummy98')
4249 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4250 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4251 self
.assertIn('DOMAINS=hogehogehoge', output
)
4252 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4253 self
.assertIn('LLMNR=yes', output
)
4254 self
.assertIn('MDNS=no', output
)
4255 self
.assertIn('DNSSEC=yes', output
)
4257 check_output(*timedatectl_cmd
, 'revert', 'dummy98', env
=env
)
4259 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4262 output
= read_link_state_file('dummy98')
4264 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4265 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4266 self
.assertIn('DOMAINS=hogehogehoge', output
)
4267 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4268 self
.assertIn('LLMNR=yes', output
)
4269 self
.assertIn('MDNS=no', output
)
4270 self
.assertIn('DNSSEC=yes', output
)
4272 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4274 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4277 output
= read_link_state_file('dummy98')
4279 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4280 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4281 self
.assertIn('DOMAINS=hogehoge', output
)
4282 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4283 self
.assertIn('LLMNR=no', output
)
4284 self
.assertIn('MDNS=yes', output
)
4285 self
.assertIn('DNSSEC=no', output
)
4287 def test_address_state(self
):
4288 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4291 self
.wait_online(['dummy98:degraded'])
4293 output
= read_link_state_file('dummy98')
4294 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4295 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4297 # with a routable IPv4 address
4298 check_output('ip address add 10.1.2.3/16 dev dummy98')
4299 self
.wait_online(['dummy98:routable'], ipv4
=True)
4300 self
.wait_online(['dummy98:routable'])
4302 output
= read_link_state_file('dummy98')
4303 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4304 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4306 check_output('ip address del 10.1.2.3/16 dev dummy98')
4308 # with a routable IPv6 address
4309 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4310 self
.wait_online(['dummy98:routable'], ipv6
=True)
4311 self
.wait_online(['dummy98:routable'])
4313 output
= read_link_state_file('dummy98')
4314 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4315 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4317 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4325 def test_bond_keep_master(self
):
4326 check_output('ip link add bond199 type bond mode active-backup')
4327 check_output('ip link add dummy98 type dummy')
4328 check_output('ip link set dummy98 master bond199')
4330 copy_network_unit('23-keep-master.network')
4332 self
.wait_online(['dummy98:enslaved'])
4334 output
= check_output('ip -d link show bond199')
4336 self
.assertRegex(output
, 'active_slave dummy98')
4338 output
= check_output('ip -d link show dummy98')
4340 self
.assertRegex(output
, 'master bond199')
4342 def test_bond_active_slave(self
):
4343 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4345 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4347 output
= check_output('ip -d link show bond199')
4349 self
.assertIn('active_slave dummy98', output
)
4351 def test_bond_primary_slave(self
):
4352 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4354 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4356 output
= check_output('ip -d link show bond199')
4358 self
.assertIn('primary dummy98', output
)
4361 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4362 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4363 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4364 f
.write(f
'[Link]\nMACAddress={mac}\n')
4367 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4369 output
= check_output('ip -d link show bond199')
4371 self
.assertIn(f
'link/ether {mac}', output
)
4373 def test_bond_operstate(self
):
4374 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4375 '25-bond99.network', '25-bond-slave.network')
4377 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4379 output
= check_output('ip -d link show dummy98')
4381 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4383 output
= check_output('ip -d link show test1')
4385 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4387 output
= check_output('ip -d link show bond99')
4389 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4391 self
.wait_operstate('dummy98', 'enslaved')
4392 self
.wait_operstate('test1', 'enslaved')
4393 self
.wait_operstate('bond99', 'routable')
4395 check_output('ip link set dummy98 down')
4397 self
.wait_operstate('dummy98', 'off')
4398 self
.wait_operstate('test1', 'enslaved')
4399 self
.wait_operstate('bond99', 'routable')
4401 check_output('ip link set dummy98 up')
4403 self
.wait_operstate('dummy98', 'enslaved')
4404 self
.wait_operstate('test1', 'enslaved')
4405 self
.wait_operstate('bond99', 'routable')
4407 check_output('ip link set dummy98 down')
4408 check_output('ip link set test1 down')
4410 self
.wait_operstate('dummy98', 'off')
4411 self
.wait_operstate('test1', 'off')
4413 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4414 # Huh? Kernel does not recognize that all slave interfaces are down?
4415 # Let's confirm that networkd's operstate is consistent with ip's result.
4416 self
.assertNotRegex(output
, 'NO-CARRIER')
4418 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4426 def test_bridge_vlan(self
):
4427 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4428 '26-bridge.netdev', '26-bridge-vlan-master.network',
4431 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4433 output
= check_output('bridge vlan show dev test1')
4435 # check if the default VID is removed
4436 self
.assertNotIn('1 Egress Untagged', output
)
4437 for i
in range(1000, 3000):
4439 self
.assertIn(f
'{i} PVID', output
)
4440 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4441 self
.assertIn(f
'{i} Egress Untagged', output
)
4442 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4443 self
.assertIn(f
'{i}', output
)
4445 self
.assertNotIn(f
'{i}', output
)
4447 output
= check_output('bridge vlan show dev bridge99')
4449 # check if the default VID is removed
4450 self
.assertNotIn('1 Egress Untagged', output
)
4451 for i
in range(1000, 3000):
4453 self
.assertIn(f
'{i} PVID', output
)
4454 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4455 self
.assertIn(f
'{i} Egress Untagged', output
)
4456 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4457 self
.assertIn(f
'{i}', output
)
4459 self
.assertNotIn(f
'{i}', output
)
4462 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4463 '26-bridge-vlan-master.network.d/10-override.conf')
4465 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4467 output
= check_output('bridge vlan show dev test1')
4469 for i
in range(1000, 3000):
4471 self
.assertIn(f
'{i} PVID', output
)
4472 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4473 self
.assertIn(f
'{i} Egress Untagged', output
)
4474 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4475 self
.assertIn(f
'{i}', output
)
4477 self
.assertNotIn(f
'{i}', output
)
4479 output
= check_output('bridge vlan show dev bridge99')
4481 for i
in range(1000, 3000):
4483 self
.assertIn(f
'{i} PVID', output
)
4484 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4485 self
.assertIn(f
'{i} Egress Untagged', output
)
4486 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4487 self
.assertIn(f
'{i}', output
)
4489 self
.assertNotIn(f
'{i}', output
)
4491 # Remove several vlan IDs
4492 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4493 '26-bridge-vlan-master.network.d/20-override.conf')
4495 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4497 output
= check_output('bridge vlan show dev test1')
4499 for i
in range(1000, 3000):
4501 self
.assertIn(f
'{i} PVID', output
)
4502 elif i
in range(2012, 2016):
4503 self
.assertIn(f
'{i} Egress Untagged', output
)
4504 elif i
in range(2008, 2014):
4505 self
.assertIn(f
'{i}', output
)
4507 self
.assertNotIn(f
'{i}', output
)
4509 output
= check_output('bridge vlan show dev bridge99')
4511 for i
in range(1000, 3000):
4513 self
.assertIn(f
'{i} PVID', output
)
4514 elif i
in range(2022, 2026):
4515 self
.assertIn(f
'{i} Egress Untagged', output
)
4516 elif i
in range(2018, 2024):
4517 self
.assertIn(f
'{i}', output
)
4519 self
.assertNotIn(f
'{i}', output
)
4521 # Remove all vlan IDs
4522 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4523 '26-bridge-vlan-master.network.d/30-override.conf')
4525 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4527 output
= check_output('bridge vlan show dev test1')
4529 self
.assertNotIn('PVID', output
)
4530 for i
in range(1000, 3000):
4531 self
.assertNotIn(f
'{i}', output
)
4533 output
= check_output('bridge vlan show dev bridge99')
4535 self
.assertNotIn('PVID', output
)
4536 for i
in range(1000, 3000):
4537 self
.assertNotIn(f
'{i}', output
)
4539 def test_bridge_vlan_issue_20373(self
):
4540 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4541 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4542 '21-vlan.netdev', '21-vlan.network')
4544 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4546 output
= check_output('bridge vlan show dev test1')
4548 self
.assertIn('100 PVID Egress Untagged', output
)
4549 self
.assertIn('560', output
)
4550 self
.assertIn('600', output
)
4552 output
= check_output('bridge vlan show dev bridge99')
4554 self
.assertIn('1 PVID Egress Untagged', output
)
4555 self
.assertIn('100', output
)
4556 self
.assertIn('600', output
)
4558 def test_bridge_mdb(self
):
4559 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4560 '26-bridge.netdev', '26-bridge-mdb-master.network')
4562 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4564 output
= check_output('bridge mdb show dev bridge99')
4566 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4567 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4569 # Old kernel may not support bridge MDB entries on bridge master
4570 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4571 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4572 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4574 def test_bridge_keep_master(self
):
4575 check_output('ip link add bridge99 type bridge')
4576 check_output('ip link set bridge99 up')
4577 check_output('ip link add dummy98 type dummy')
4578 check_output('ip link set dummy98 master bridge99')
4580 copy_network_unit('23-keep-master.network')
4582 self
.wait_online(['dummy98:enslaved'])
4584 output
= check_output('ip -d link show dummy98')
4586 self
.assertRegex(output
, 'master bridge99')
4587 self
.assertRegex(output
, 'bridge')
4589 output
= check_output('bridge -d link show dummy98')
4591 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4592 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4593 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4594 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4595 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4596 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4597 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4598 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4599 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4600 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4601 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4602 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4604 def test_bridge_property(self
):
4605 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4606 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4607 '25-bridge99.network')
4609 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4611 output
= check_output('ip -d link show bridge99')
4613 self
.assertIn('mtu 9000 ', output
)
4615 output
= check_output('ip -d link show test1')
4617 self
.assertIn('master bridge99 ', output
)
4618 self
.assertIn('bridge_slave', output
)
4619 self
.assertIn('mtu 9000 ', output
)
4621 output
= check_output('ip -d link show dummy98')
4623 self
.assertIn('master bridge99 ', output
)
4624 self
.assertIn('bridge_slave', output
)
4625 self
.assertIn('mtu 9000 ', output
)
4627 output
= check_output('ip addr show bridge99')
4629 self
.assertIn('192.168.0.15/24', output
)
4631 output
= check_output('bridge -d link show dummy98')
4633 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4634 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4635 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4636 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4637 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4638 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4639 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4640 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4641 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4642 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4643 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4644 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4645 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4647 output
= check_output('bridge -d link show test1')
4649 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4651 check_output('ip address add 192.168.0.16/24 dev bridge99')
4652 output
= check_output('ip addr show bridge99')
4654 self
.assertIn('192.168.0.16/24', output
)
4657 print('### ip -6 route list table all dev bridge99')
4658 output
= check_output('ip -6 route list table all dev bridge99')
4660 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4662 remove_link('test1')
4663 self
.wait_operstate('bridge99', 'routable')
4665 output
= check_output('ip -d link show bridge99')
4667 self
.assertIn('mtu 9000 ', output
)
4669 output
= check_output('ip -d link show dummy98')
4671 self
.assertIn('master bridge99 ', output
)
4672 self
.assertIn('bridge_slave', output
)
4673 self
.assertIn('mtu 9000 ', output
)
4675 remove_link('dummy98')
4676 self
.wait_operstate('bridge99', 'no-carrier')
4678 output
= check_output('ip -d link show bridge99')
4680 # When no carrier, the kernel may reset the MTU
4681 self
.assertIn('NO-CARRIER', output
)
4683 output
= check_output('ip address show bridge99')
4685 self
.assertNotIn('192.168.0.15/24', output
)
4686 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4688 print('### ip -6 route list table all dev bridge99')
4689 output
= check_output('ip -6 route list table all dev bridge99')
4691 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4693 check_output('ip link add dummy98 type dummy')
4694 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4696 output
= check_output('ip -d link show bridge99')
4698 self
.assertIn('mtu 9000 ', output
)
4700 output
= check_output('ip -d link show dummy98')
4702 self
.assertIn('master bridge99 ', output
)
4703 self
.assertIn('bridge_slave', output
)
4704 self
.assertIn('mtu 9000 ', output
)
4706 def test_bridge_configure_without_carrier(self
):
4707 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4711 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4712 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4713 with self
.subTest(test
=test
):
4714 if test
== 'no-slave':
4715 # bridge has no slaves; it's up but *might* not have carrier
4716 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4717 # due to a bug in the kernel, newly-created bridges are brought up
4718 # *with* carrier, unless they have had any setting changed; e.g.
4719 # their mac set, priority set, etc. Then, they will lose carrier
4720 # as soon as a (down) slave interface is added, and regain carrier
4721 # again once the slave interface is brought up.
4722 #self.check_link_attr('bridge99', 'carrier', '0')
4723 elif test
== 'add-slave':
4724 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4725 self
.check_link_attr('test1', 'operstate', 'down')
4726 check_output('ip link set dev test1 master bridge99')
4727 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4728 self
.check_link_attr('bridge99', 'carrier', '0')
4729 elif test
== 'slave-up':
4730 # bring up slave, which will have carrier; bridge gains carrier
4731 check_output('ip link set dev test1 up')
4732 self
.wait_online(['bridge99:routable'])
4733 self
.check_link_attr('bridge99', 'carrier', '1')
4734 elif test
== 'slave-no-carrier':
4735 # drop slave carrier; bridge loses carrier
4736 check_output('ip link set dev test1 carrier off')
4737 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4738 self
.check_link_attr('bridge99', 'carrier', '0')
4739 elif test
== 'slave-carrier':
4740 # restore slave carrier; bridge gains carrier
4741 check_output('ip link set dev test1 carrier on')
4742 self
.wait_online(['bridge99:routable'])
4743 self
.check_link_attr('bridge99', 'carrier', '1')
4744 elif test
== 'slave-down':
4745 # bring down slave; bridge loses carrier
4746 check_output('ip link set dev test1 down')
4747 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4748 self
.check_link_attr('bridge99', 'carrier', '0')
4750 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
4751 self
.assertRegex(output
, '10.1.2.3')
4752 self
.assertRegex(output
, '10.1.2.1')
4754 def test_bridge_ignore_carrier_loss(self
):
4755 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4756 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4757 '25-bridge99-ignore-carrier-loss.network')
4759 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4761 check_output('ip address add 192.168.0.16/24 dev bridge99')
4762 remove_link('test1', 'dummy98')
4765 output
= check_output('ip address show bridge99')
4767 self
.assertRegex(output
, 'NO-CARRIER')
4768 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4769 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
4771 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
4772 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4773 '25-bridge99-ignore-carrier-loss.network')
4775 self
.wait_online(['bridge99:no-carrier'])
4777 for trial
in range(4):
4778 check_output('ip link add dummy98 type dummy')
4779 check_output('ip link set dummy98 up')
4781 remove_link('dummy98')
4783 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4785 output
= check_output('ip address show bridge99')
4787 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4789 output
= check_output('ip rule list table 100')
4791 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
4793 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
4801 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4802 def test_sriov(self
):
4803 copy_network_unit('25-default.link', '25-sriov.network')
4805 call('modprobe netdevsim')
4807 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4810 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
4814 self
.wait_online(['eni99np1:routable'])
4816 output
= check_output('ip link show dev eni99np1')
4818 self
.assertRegex(output
,
4819 '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 *'
4820 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4821 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4824 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4825 def test_sriov_udev(self
):
4826 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
4828 call('modprobe netdevsim')
4830 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4834 self
.wait_online(['eni99np1:routable'])
4836 # the name eni99np1 may be an alternative name.
4837 ifname
= link_resolve('eni99np1')
4839 output
= check_output('ip link show dev eni99np1')
4841 self
.assertRegex(output
,
4842 '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 *'
4843 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4844 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4846 self
.assertNotIn('vf 3', output
)
4847 self
.assertNotIn('vf 4', output
)
4849 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4850 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
4853 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4855 output
= check_output('ip link show dev eni99np1')
4857 self
.assertRegex(output
,
4858 '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 *'
4859 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4860 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4863 self
.assertNotIn('vf 4', output
)
4865 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4866 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4869 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4871 output
= check_output('ip link show dev eni99np1')
4873 self
.assertRegex(output
,
4874 '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 *'
4875 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4876 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4879 self
.assertNotIn('vf 4', output
)
4881 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4882 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
4885 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4887 output
= check_output('ip link show dev eni99np1')
4889 self
.assertRegex(output
,
4890 '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 *'
4891 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
4893 self
.assertNotIn('vf 2', output
)
4894 self
.assertNotIn('vf 3', output
)
4895 self
.assertNotIn('vf 4', output
)
4897 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4898 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4901 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4903 output
= check_output('ip link show dev eni99np1')
4905 self
.assertRegex(output
,
4906 '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 *'
4907 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4908 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4910 self
.assertNotIn('vf 3', output
)
4911 self
.assertNotIn('vf 4', output
)
4913 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
4921 def test_lldp(self
):
4922 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
4924 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
4926 for trial
in range(10):
4930 output
= check_output(*networkctl_cmd
, 'lldp', env
=env
)
4932 if re
.search(r
'veth99 .* veth-peer', output
):
4937 class NetworkdRATests(unittest
.TestCase
, Utilities
):
4945 def test_ipv6_prefix_delegation(self
):
4946 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
4947 self
.setup_nftset('addr6', 'ipv6_addr')
4948 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
4949 self
.setup_nftset('ifindex', 'iface_index')
4951 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4953 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4955 self
.assertRegex(output
, 'fe80::')
4956 self
.assertRegex(output
, '2002:da8:1::1')
4958 output
= check_output(*resolvectl_cmd
, 'domain', 'veth99', env
=env
)
4960 self
.assertIn('hogehoge.test', output
)
4962 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4964 self
.assertRegex(output
, '2002:da8:1:0')
4966 self
.check_netlabel('veth99', '2002:da8:1::/64')
4967 self
.check_netlabel('veth99', '2002:da8:2::/64')
4969 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
4970 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
4971 self
.check_nftset('network6', '2002:da8:1::/64')
4972 self
.check_nftset('network6', '2002:da8:2::/64')
4973 self
.check_nftset('ifindex', 'veth99')
4975 self
.teardown_nftset('addr6', 'network6', 'ifindex')
4977 def test_ipv6_token_static(self
):
4978 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
4980 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4982 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4984 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
4985 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
4986 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
4987 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
4989 def test_ipv6_token_prefixstable(self
):
4990 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
4992 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4994 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4996 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
4997 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
4999 def test_ipv6_token_prefixstable_without_address(self
):
5000 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5002 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5004 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5006 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5007 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5009 def test_router_preference(self
):
5010 copy_network_unit('25-veth-client.netdev',
5011 '25-veth-router-high.netdev',
5012 '25-veth-router-low.netdev',
5014 '25-veth-bridge.network',
5015 '25-veth-client.network',
5016 '25-veth-router-high.network',
5017 '25-veth-router-low.network',
5018 '25-bridge99.network')
5020 self
.wait_online(['client-p:enslaved',
5021 'router-high:degraded', 'router-high-p:enslaved',
5022 'router-low:degraded', 'router-low-p:enslaved',
5023 'bridge99:routable'])
5025 networkctl_reconfigure('client')
5026 self
.wait_online(['client:routable'])
5028 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5029 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5030 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5031 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5033 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5035 self
.assertIn('pref high', output
)
5036 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5038 self
.assertIn('pref low', output
)
5040 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5041 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5044 self
.wait_online(['client:routable'])
5046 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5047 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5048 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5049 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5051 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5053 self
.assertIn('pref high', output
)
5054 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5056 self
.assertIn('pref low', output
)
5058 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5059 def test_captive_portal(self
):
5060 copy_network_unit('25-veth-client.netdev',
5061 '25-veth-router-captive.netdev',
5063 '25-veth-client-captive.network',
5064 '25-veth-router-captive.network',
5065 '25-veth-bridge-captive.network',
5066 '25-bridge99.network')
5068 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5069 'router-captive:degraded', 'router-captivep:enslaved'])
5071 start_radvd(config_file
='captive-portal.conf')
5072 networkctl_reconfigure('client')
5073 self
.wait_online(['client:routable'])
5075 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5076 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5078 self
.assertIn('Captive Portal: http://systemd.io', output
)
5080 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5081 def test_invalid_captive_portal(self
):
5082 def radvd_write_config(captive_portal_uri
):
5083 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5084 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5086 captive_portal_uris
= [
5087 "42ěščěškd ěšč ě s",
5092 copy_network_unit('25-veth-client.netdev',
5093 '25-veth-router-captive.netdev',
5095 '25-veth-client-captive.network',
5096 '25-veth-router-captive.network',
5097 '25-veth-bridge-captive.network',
5098 '25-bridge99.network')
5100 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5101 'router-captive:degraded', 'router-captivep:enslaved'])
5103 for uri
in captive_portal_uris
:
5104 print(f
"Captive portal: {uri}")
5105 radvd_write_config(uri
)
5107 start_radvd(config_file
='bogus-captive-portal.conf')
5108 networkctl_reconfigure('client')
5109 self
.wait_online(['client:routable'])
5111 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5112 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5114 self
.assertNotIn('Captive Portal:', output
)
5116 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5124 def test_dhcp_server(self
):
5125 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5127 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5129 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5131 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5132 self
.assertIn('Gateway: 192.168.5.3', output
)
5133 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5134 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5136 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5137 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5139 def test_dhcp_server_null_server_address(self
):
5140 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5142 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5144 output
= check_output('ip --json address show dev veth-peer')
5145 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5146 print(server_address
)
5148 output
= check_output('ip --json address show dev veth99')
5149 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5150 print(client_address
)
5152 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5154 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5155 self
.assertIn(f
'Gateway: {server_address}', output
)
5156 self
.assertIn(f
'DNS: {server_address}', output
)
5157 self
.assertIn(f
'NTP: {server_address}', output
)
5159 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5160 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5162 def test_dhcp_server_with_uplink(self
):
5163 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5164 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5166 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5168 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5170 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5171 self
.assertIn('Gateway: 192.168.5.3', output
)
5172 self
.assertIn('DNS: 192.168.5.1', output
)
5173 self
.assertIn('NTP: 192.168.5.1', output
)
5175 def test_emit_router_timezone(self
):
5176 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5178 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5180 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5182 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5183 self
.assertIn('Gateway: 192.168.5.1', output
)
5184 self
.assertIn('Time Zone: Europe/Berlin', output
)
5186 def test_dhcp_server_static_lease(self
):
5187 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5189 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5191 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5193 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5194 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5196 def test_dhcp_server_static_lease_default_client_id(self
):
5197 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5199 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5201 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5203 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5204 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5206 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5214 def test_relay_agent(self
):
5215 copy_network_unit('25-agent-veth-client.netdev',
5216 '25-agent-veth-server.netdev',
5217 '25-agent-client.network',
5218 '25-agent-server.network',
5219 '25-agent-client-peer.network',
5220 '25-agent-server-peer.network')
5223 self
.wait_online(['client:routable'])
5225 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'client', env
=env
)
5227 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5229 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5237 def test_dhcp_client_ipv6_only(self
):
5238 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5241 self
.wait_online(['veth-peer:carrier'])
5243 # information request mode
5244 # The name ipv6-only option may not be supported by older dnsmasq
5245 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5246 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5247 '--dhcp-option=option6:dns-server,[2600::ee]',
5248 '--dhcp-option=option6:ntp-server,[2600::ff]',
5249 ra_mode
='ra-stateless')
5250 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5252 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5253 # Let's wait for the expected DNS server being listed in the state file.
5254 for _
in range(100):
5255 output
= read_link_state_file('veth99')
5256 if 'DNS=2600::ee' in output
:
5260 # Check link state file
5261 print('## link state file')
5262 output
= read_link_state_file('veth99')
5264 self
.assertIn('DNS=2600::ee', output
)
5265 self
.assertIn('NTP=2600::ff', output
)
5267 # Check manager state file
5268 print('## manager state file')
5269 output
= read_manager_state_file()
5271 self
.assertRegex(output
, 'DNS=.*2600::ee')
5272 self
.assertRegex(output
, 'NTP=.*2600::ff')
5274 print('## dnsmasq log')
5275 output
= read_dnsmasq_log_file()
5277 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5278 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5279 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5280 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5281 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5284 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5289 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5290 '--dhcp-option=option6:dns-server,[2600::ee]',
5291 '--dhcp-option=option6:ntp-server,[2600::ff]')
5292 networkctl_reconfigure('veth99')
5293 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5296 output
= check_output('ip address show dev veth99 scope global')
5298 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5299 self
.assertNotIn('192.168.5', output
)
5301 # checking semi-static route
5302 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5304 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5306 # Confirm that ipv6 token is not set in the kernel
5307 output
= check_output('ip token show dev veth99')
5309 self
.assertRegex(output
, 'token :: dev veth99')
5311 # Make manager and link state file updated
5312 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5314 # Check link state file
5315 print('## link state file')
5316 output
= read_link_state_file('veth99')
5318 self
.assertIn('DNS=2600::ee', output
)
5319 self
.assertIn('NTP=2600::ff', output
)
5321 # Check manager state file
5322 print('## manager state file')
5323 output
= read_manager_state_file()
5325 self
.assertRegex(output
, 'DNS=.*2600::ee')
5326 self
.assertRegex(output
, 'NTP=.*2600::ff')
5328 print('## dnsmasq log')
5329 output
= read_dnsmasq_log_file()
5331 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5332 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5333 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5334 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5335 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5336 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5339 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5342 # Testing without rapid commit support
5343 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5344 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5347 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5348 '--dhcp-option=option6:dns-server,[2600::ee]',
5349 '--dhcp-option=option6:ntp-server,[2600::ff]')
5352 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5355 output
= check_output('ip address show dev veth99 scope global')
5357 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5358 self
.assertNotIn('192.168.5', output
)
5360 # checking semi-static route
5361 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5363 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5365 # Make manager and link state file updated
5366 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5368 # Check link state file
5369 print('## link state file')
5370 output
= read_link_state_file('veth99')
5372 self
.assertIn('DNS=2600::ee', output
)
5373 self
.assertIn('NTP=2600::ff', output
)
5375 # Check manager state file
5376 print('## manager state file')
5377 output
= read_manager_state_file()
5379 self
.assertRegex(output
, 'DNS=.*2600::ee')
5380 self
.assertRegex(output
, 'NTP=.*2600::ff')
5382 print('## dnsmasq log')
5383 output
= read_dnsmasq_log_file()
5385 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5386 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5387 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5388 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5389 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5390 self
.assertNotIn('rapid-commit', output
)
5393 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5396 def test_dhcp_client_ipv6_dbus_status(self
):
5397 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5399 self
.wait_online(['veth-peer:carrier'])
5401 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5402 # bit set) has yet been received and the configuration does not include WithoutRA=true
5403 state
= get_dhcp6_client_state('veth99')
5404 print(f
"DHCPv6 client state = {state}")
5405 self
.assertEqual(state
, 'stopped')
5407 state
= get_dhcp4_client_state('veth99')
5408 print(f
"DHCPv4 client state = {state}")
5409 self
.assertEqual(state
, 'selecting')
5411 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5412 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5414 state
= get_dhcp6_client_state('veth99')
5415 print(f
"DHCPv6 client state = {state}")
5416 self
.assertEqual(state
, 'bound')
5418 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5419 for _
in range(100):
5420 state
= get_dhcp4_client_state('veth99')
5421 if state
== 'stopped':
5425 print(f
"DHCPv4 client state = {state}")
5426 self
.assertEqual(state
, 'stopped')
5428 # restart dnsmasq to clear log
5430 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5432 # Test renew command
5433 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5434 check_output(*networkctl_cmd
, 'renew', 'veth99', env
=env
)
5436 for _
in range(100):
5437 state
= get_dhcp4_client_state('veth99')
5438 if state
== 'stopped':
5442 print(f
"DHCPv4 client state = {state}")
5443 self
.assertEqual(state
, 'stopped')
5445 print('## dnsmasq log')
5446 output
= read_dnsmasq_log_file()
5448 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5449 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5450 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5451 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5453 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5454 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5457 self
.wait_online(['veth-peer:carrier'])
5459 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5462 output
= check_output('ip address show dev veth99 scope global')
5464 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5465 self
.assertNotIn('192.168.5', output
)
5467 print('## dnsmasq log')
5468 output
= read_dnsmasq_log_file()
5470 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5471 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5472 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5473 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5474 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5476 def test_dhcp_client_ipv4_only(self
):
5477 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5479 self
.setup_nftset('addr4', 'ipv4_addr')
5480 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5481 self
.setup_nftset('ifindex', 'iface_index')
5484 self
.wait_online(['veth-peer:carrier'])
5485 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5486 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5487 '--dhcp-option=option:domain-search,example.com',
5488 '--dhcp-alternate-port=67,5555',
5489 ipv4_range
='192.168.5.110,192.168.5.119')
5490 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5491 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5493 print('## ip address show dev veth99 scope global')
5494 output
= check_output('ip address show dev veth99 scope global')
5496 self
.assertIn('mtu 1492', output
)
5497 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5498 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')
5499 self
.assertNotIn('2600::', output
)
5501 output
= check_output('ip -4 --json address show dev veth99')
5502 for i
in json
.loads(output
)[0]['addr_info']:
5503 if i
['label'] == 'test-label':
5504 address1
= i
['local']
5507 self
.assertFalse(True)
5509 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5511 print('## ip route show table main dev veth99')
5512 output
= check_output('ip route show table main dev veth99')
5514 # no DHCP routes assigned to the main table
5515 self
.assertNotIn('proto dhcp', output
)
5517 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5518 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5519 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5520 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5522 print('## ip route show table 211 dev veth99')
5523 output
= check_output('ip route show table 211 dev veth99')
5525 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5526 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5527 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5528 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5529 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5530 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5532 print('## link state file')
5533 output
= read_link_state_file('veth99')
5535 # checking DNS server, SIP server, and Domains
5536 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5537 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5538 self
.assertIn('DOMAINS=example.com', output
)
5541 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5542 j
= json
.loads(output
)
5544 self
.assertEqual(len(j
['DNS']), 2)
5547 self
.assertEqual(i
['Family'], 2)
5548 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5549 self
.assertRegex(a
, '^192.168.5.[67]$')
5550 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5551 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5552 self
.assertEqual('192.168.5.1', a
)
5554 self
.assertEqual(len(j
['SIP']), 2)
5557 self
.assertEqual(i
['Family'], 2)
5558 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5559 self
.assertRegex(a
, '^192.168.5.2[12]$')
5560 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5561 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5562 self
.assertEqual('192.168.5.1', a
)
5564 print('## dnsmasq log')
5565 output
= read_dnsmasq_log_file()
5567 self
.assertIn('vendor class: FooBarVendorTest', output
)
5568 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5569 self
.assertIn('client provides name: test-hostname', output
)
5570 self
.assertIn('26:mtu', output
)
5572 # change address range, DNS servers, and Domains
5574 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5575 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5576 '--dhcp-option=option:domain-search,foo.example.com',
5577 '--dhcp-alternate-port=67,5555',
5578 ipv4_range
='192.168.5.120,192.168.5.129',)
5580 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5581 print('Wait for the DHCP lease to be expired')
5582 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5583 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5585 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5587 print('## ip address show dev veth99 scope global')
5588 output
= check_output('ip address show dev veth99 scope global')
5590 self
.assertIn('mtu 1492', output
)
5591 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5592 self
.assertNotIn(f
'{address1}', output
)
5593 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')
5594 self
.assertNotIn('2600::', output
)
5596 output
= check_output('ip -4 --json address show dev veth99')
5597 for i
in json
.loads(output
)[0]['addr_info']:
5598 if i
['label'] == 'test-label':
5599 address2
= i
['local']
5602 self
.assertFalse(True)
5604 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5606 print('## ip route show table main dev veth99')
5607 output
= check_output('ip route show table main dev veth99')
5609 # no DHCP routes assigned to the main table
5610 self
.assertNotIn('proto dhcp', output
)
5612 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5613 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5614 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5615 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5617 print('## ip route show table 211 dev veth99')
5618 output
= check_output('ip route show table 211 dev veth99')
5620 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5621 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5622 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5623 self
.assertNotIn('192.168.5.6', output
)
5624 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5625 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5626 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5628 print('## link state file')
5629 output
= read_link_state_file('veth99')
5631 # checking DNS server, SIP server, and Domains
5632 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5633 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5634 self
.assertIn('DOMAINS=foo.example.com', output
)
5637 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5638 j
= json
.loads(output
)
5640 self
.assertEqual(len(j
['DNS']), 3)
5643 self
.assertEqual(i
['Family'], 2)
5644 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5645 self
.assertRegex(a
, '^192.168.5.[178]$')
5646 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5647 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5648 self
.assertEqual('192.168.5.1', a
)
5650 self
.assertEqual(len(j
['SIP']), 2)
5653 self
.assertEqual(i
['Family'], 2)
5654 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5655 self
.assertRegex(a
, '^192.168.5.2[34]$')
5656 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5657 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5658 self
.assertEqual('192.168.5.1', a
)
5660 print('## dnsmasq log')
5661 output
= read_dnsmasq_log_file()
5663 self
.assertIn('vendor class: FooBarVendorTest', output
)
5664 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5665 self
.assertIn('client provides name: test-hostname', output
)
5666 self
.assertIn('26:mtu', output
)
5668 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5670 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5671 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5672 self
.check_nftset('ifindex', 'veth99')
5674 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5676 def test_dhcp_client_ipv4_dbus_status(self
):
5677 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5679 self
.wait_online(['veth-peer:carrier'])
5681 state
= get_dhcp4_client_state('veth99')
5682 print(f
"State = {state}")
5683 self
.assertEqual(state
, 'rebooting')
5685 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5686 '--dhcp-option=option:domain-search,example.com',
5687 '--dhcp-alternate-port=67,5555',
5688 ipv4_range
='192.168.5.110,192.168.5.119')
5689 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5690 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5692 state
= get_dhcp4_client_state('veth99')
5693 print(f
"State = {state}")
5694 self
.assertEqual(state
, 'bound')
5696 def test_dhcp_client_allow_list(self
):
5697 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5700 self
.wait_online(['veth-peer:carrier'])
5701 since
= datetime
.datetime
.now()
5704 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5706 if expect
in read_networkd_log(since
=since
):
5712 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
5713 since
= datetime
.datetime
.now()
5716 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5718 if expect
in read_networkd_log(since
=since
):
5724 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
5725 since
= datetime
.datetime
.now()
5728 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
5730 if expect
in read_networkd_log(since
=since
):
5736 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
5737 def test_dhcp_client_rapid_commit(self
):
5738 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5740 self
.wait_online(['veth-peer:carrier'])
5742 start_dnsmasq('--dhcp-rapid-commit')
5743 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5744 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5746 state
= get_dhcp4_client_state('veth99')
5747 print(f
"DHCPv4 client state = {state}")
5748 self
.assertEqual(state
, 'bound')
5750 output
= read_dnsmasq_log_file()
5751 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
5752 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
5753 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5754 self
.assertIn('DHCPACK(veth-peer)', output
)
5756 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
5757 copy_network_unit('25-veth.netdev',
5758 '25-dhcp-server-ipv6-only-mode.network',
5759 '25-dhcp-client-ipv6-only-mode.network')
5761 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
5762 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5764 state
= get_dhcp4_client_state('veth99')
5765 print(f
"State = {state}")
5766 self
.assertEqual(state
, 'bound')
5768 def test_dhcp_client_ipv4_use_routes_gateway(self
):
5770 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
5776 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
5777 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
5778 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
5780 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
5781 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
5782 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
5783 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
5784 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
5785 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
5786 copy_network_unit(*testunits
, copy_dropins
=False)
5789 self
.wait_online(['veth-peer:carrier'])
5790 additional_options
= [
5791 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
5792 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
5793 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
5796 additional_options
+= [
5797 '--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'
5799 start_dnsmasq(*additional_options
)
5800 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5802 output
= check_output('ip -4 route show dev veth99')
5808 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5809 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5810 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5811 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5812 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5814 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5815 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5816 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5817 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5819 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5820 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5821 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5822 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5823 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5824 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5825 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5826 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5829 if use_gateway
and (not classless
or not use_routes
):
5830 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5832 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5834 # Check route to gateway
5835 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
5836 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5838 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5840 # Check RoutesToDNS= and RoutesToNTP=
5841 if dns_and_ntp_routes
:
5842 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5843 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5846 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5847 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5849 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5850 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5852 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5853 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5855 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5856 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5857 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5858 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5860 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
5863 def test_dhcp_client_settings_anonymize(self
):
5864 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
5866 self
.wait_online(['veth-peer:carrier'])
5868 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5870 print('## dnsmasq log')
5871 output
= read_dnsmasq_log_file()
5873 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
5874 self
.assertNotIn('test-hostname', output
)
5875 self
.assertNotIn('26:mtu', output
)
5877 def test_dhcp_keep_configuration_dhcp(self
):
5878 copy_network_unit('25-veth.netdev',
5879 '25-dhcp-server-veth-peer.network',
5880 '25-dhcp-client-keep-configuration-dhcp.network')
5882 self
.wait_online(['veth-peer:carrier'])
5884 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5886 output
= check_output('ip address show dev veth99 scope global')
5888 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5889 'valid_lft forever preferred_lft forever')
5891 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
5894 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5895 print('Wait for the DHCP lease to be expired')
5898 # The lease address should be kept after the lease expired
5899 output
= check_output('ip address show dev veth99 scope global')
5901 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5902 'valid_lft forever preferred_lft forever')
5906 # The lease address should be kept after networkd stopped
5907 output
= check_output('ip address show dev veth99 scope global')
5909 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5910 'valid_lft forever preferred_lft forever')
5912 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
5913 f
.write('[Network]\nDHCP=no\n')
5916 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5918 # Still the lease address should be kept after networkd restarted
5919 output
= check_output('ip address show dev veth99 scope global')
5921 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5922 'valid_lft forever preferred_lft forever')
5924 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
5925 copy_network_unit('25-veth.netdev',
5926 '25-dhcp-server-veth-peer.network',
5927 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
5929 self
.wait_online(['veth-peer:carrier'])
5931 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5933 output
= check_output('ip address show dev veth99 scope global')
5935 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5940 output
= check_output('ip address show dev veth99 scope global')
5942 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5945 self
.wait_online(['veth-peer:routable'])
5947 output
= check_output('ip address show dev veth99 scope global')
5949 self
.assertNotIn('192.168.5.', output
)
5951 def test_dhcp_client_reuse_address_as_static(self
):
5952 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5954 self
.wait_online(['veth-peer:carrier'])
5956 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5958 # link become 'routable' when at least one protocol provide an valid address.
5959 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
5960 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
5962 output
= check_output('ip address show dev veth99 scope global')
5963 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
5964 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
5965 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
5966 print(static_network
)
5968 remove_network_unit('25-dhcp-client.network')
5970 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
5971 f
.write(static_network
)
5974 self
.wait_online(['veth99:routable'])
5976 output
= check_output('ip -4 address show dev veth99 scope global')
5978 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
5979 'valid_lft forever preferred_lft forever')
5981 output
= check_output('ip -6 address show dev veth99 scope global')
5983 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
5984 'valid_lft forever preferred_lft forever')
5986 @expectedFailureIfModuleIsNotAvailable('vrf')
5987 def test_dhcp_client_vrf(self
):
5988 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
5989 '25-vrf.netdev', '25-vrf.network')
5991 self
.wait_online(['veth-peer:carrier'])
5993 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
5995 # link become 'routable' when at least one protocol provide an valid address.
5996 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
5997 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
5999 print('## ip -d link show dev vrf99')
6000 output
= check_output('ip -d link show dev vrf99')
6002 self
.assertRegex(output
, 'vrf table 42')
6004 print('## ip address show vrf vrf99')
6005 output
= check_output('ip address show vrf vrf99')
6007 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6008 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6009 self
.assertRegex(output
, 'inet6 .* scope link')
6011 print('## ip address show dev veth99')
6012 output
= check_output('ip address show dev veth99')
6014 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6015 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6016 self
.assertRegex(output
, 'inet6 .* scope link')
6018 print('## ip route show vrf vrf99')
6019 output
= check_output('ip route show vrf vrf99')
6021 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6022 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6023 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6025 print('## ip route show table main dev veth99')
6026 output
= check_output('ip route show table main dev veth99')
6028 self
.assertEqual(output
, '')
6030 def test_dhcp_client_gateway_onlink_implicit(self
):
6031 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6032 '25-dhcp-client-gateway-onlink-implicit.network')
6034 self
.wait_online(['veth-peer:carrier'])
6036 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6038 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
6040 self
.assertRegex(output
, '192.168.5')
6042 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6044 self
.assertRegex(output
, 'onlink')
6045 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6047 self
.assertRegex(output
, 'onlink')
6049 def test_dhcp_client_with_ipv4ll(self
):
6050 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6051 '25-dhcp-client-with-ipv4ll.network')
6053 # we need to increase timeout above default, as this will need to wait for
6054 # systemd-networkd to get the dhcpv4 transient failure event
6055 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6057 output
= check_output('ip -4 address show dev veth99')
6059 self
.assertNotIn('192.168.5.', output
)
6060 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6063 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6064 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6065 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')
6066 self
.wait_online(['veth99:routable'])
6068 output
= check_output('ip -4 address show dev veth99')
6070 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6071 self
.assertNotIn('169.254.', output
)
6072 self
.assertNotIn('scope link', output
)
6075 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6076 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)
6077 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6079 output
= check_output('ip -4 address show dev veth99')
6081 self
.assertNotIn('192.168.5.', output
)
6082 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6084 def test_dhcp_client_use_dns(self
):
6085 def check(self
, ipv4
, ipv6
):
6086 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6087 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6088 f
.write('[DHCPv4]\nUseDNS=')
6089 f
.write('yes' if ipv4
else 'no')
6090 f
.write('\n[DHCPv6]\nUseDNS=')
6091 f
.write('yes' if ipv6
else 'no')
6092 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6095 self
.wait_online(['veth99:routable'])
6097 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6098 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6099 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6101 # make resolved re-read the link state file
6102 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
6104 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
6107 self
.assertIn('192.168.5.1', output
)
6109 self
.assertNotIn('192.168.5.1', output
)
6111 self
.assertIn('2600::1', output
)
6113 self
.assertNotIn('2600::1', output
)
6115 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6118 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6121 self
.wait_online(['veth-peer:carrier'])
6122 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6123 '--dhcp-option=option6:dns-server,[2600::1]')
6125 check(self
, True, True)
6126 check(self
, True, False)
6127 check(self
, False, True)
6128 check(self
, False, False)
6130 def test_dhcp_client_use_captive_portal(self
):
6131 def check(self
, ipv4
, ipv6
):
6132 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6133 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6134 f
.write('[DHCPv4]\nUseCaptivePortal=')
6135 f
.write('yes' if ipv4
else 'no')
6136 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6137 f
.write('yes' if ipv6
else 'no')
6138 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6141 self
.wait_online(['veth99:routable'])
6143 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6144 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6145 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6147 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6150 self
.assertIn('Captive Portal: http://systemd.io', output
)
6152 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6154 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6157 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6160 self
.wait_online(['veth-peer:carrier'])
6161 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6162 '--dhcp-option=option6:103,http://systemd.io')
6164 check(self
, True, True)
6165 check(self
, True, False)
6166 check(self
, False, True)
6167 check(self
, False, False)
6169 def test_dhcp_client_reject_captive_portal(self
):
6170 def check(self
, ipv4
, ipv6
):
6171 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6172 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6173 f
.write('[DHCPv4]\nUseCaptivePortal=')
6174 f
.write('yes' if ipv4
else 'no')
6175 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6176 f
.write('yes' if ipv6
else 'no')
6177 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6180 self
.wait_online(['veth99:routable'])
6182 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6183 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6184 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6186 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6188 self
.assertNotIn('Captive Portal: ', output
)
6189 self
.assertNotIn('invalid/url', output
)
6191 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6194 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6197 self
.wait_online(['veth-peer:carrier'])
6198 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6199 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6200 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6202 check(self
, True, True)
6203 check(self
, True, False)
6204 check(self
, False, True)
6205 check(self
, False, False)
6207 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6215 def test_dhcp6pd(self
):
6216 def get_dhcp6_prefix(link
):
6217 description
= get_link_description(link
)
6219 self
.assertIn('DHCPv6Client', description
.keys())
6220 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6222 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6226 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6227 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6228 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6229 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6230 '25-dhcp-pd-downstream-dummy97.network',
6231 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6232 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6235 self
.setup_nftset('addr6', 'ipv6_addr')
6236 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6237 self
.setup_nftset('ifindex', 'iface_index')
6240 self
.wait_online(['veth-peer:routable'])
6241 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6242 self
.wait_online(['veth99:degraded'])
6244 # First, test UseAddress=no and Assign=no (issue #29979).
6245 # Note, due to the bug #29701, this test must be done at first.
6246 print('### ip -6 address show dev veth99 scope global')
6247 output
= check_output('ip -6 address show dev veth99 scope global')
6249 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6251 # Check DBus assigned prefix information to veth99
6252 prefixInfo
= get_dhcp6_prefix('veth99')
6254 self
.assertEqual(len(prefixInfo
), 1)
6255 prefixInfo
= prefixInfo
[0]
6257 self
.assertIn('Prefix', prefixInfo
.keys())
6258 self
.assertIn('PrefixLength', prefixInfo
.keys())
6259 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6260 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6262 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6263 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6264 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6265 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6267 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6269 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6270 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6272 print('### ip -6 address show dev veth-peer scope global')
6273 output
= check_output('ip -6 address show dev veth-peer scope global')
6275 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6279 # dummy97: 0x01 (The link will appear later)
6281 # dummy99: auto -> 0x02 (No address assignment)
6286 print('### ip -6 address show dev veth99 scope global')
6287 output
= check_output('ip -6 address show dev veth99 scope global')
6290 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6291 # address in IA_PD (Token=static)
6292 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6293 # address in IA_PD (Token=eui64)
6294 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6295 # address in IA_PD (temporary)
6296 # Note that the temporary addresses may appear after the link enters configured state
6297 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')
6299 print('### ip -6 address show dev test1 scope global')
6300 output
= check_output('ip -6 address show dev test1 scope global')
6302 # address in IA_PD (Token=static)
6303 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6304 # address in IA_PD (temporary)
6305 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')
6307 print('### ip -6 address show dev dummy98 scope global')
6308 output
= check_output('ip -6 address show dev dummy98 scope global')
6310 # address in IA_PD (Token=static)
6311 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6312 # address in IA_PD (temporary)
6313 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')
6315 print('### ip -6 address show dev dummy99 scope global')
6316 output
= check_output('ip -6 address show dev dummy99 scope global')
6319 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6321 print('### ip -6 address show dev veth97 scope global')
6322 output
= check_output('ip -6 address show dev veth97 scope global')
6324 # address in IA_PD (Token=static)
6325 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6326 # address in IA_PD (Token=eui64)
6327 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6328 # address in IA_PD (temporary)
6329 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')
6331 print('### ip -6 address show dev veth97-peer scope global')
6332 output
= check_output('ip -6 address show dev veth97-peer scope global')
6334 # NDisc address (Token=static)
6335 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6336 # NDisc address (Token=eui64)
6337 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6338 # NDisc address (temporary)
6339 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')
6341 print('### ip -6 address show dev veth98 scope global')
6342 output
= check_output('ip -6 address show dev veth98 scope global')
6344 # address in IA_PD (Token=static)
6345 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6346 # address in IA_PD (Token=eui64)
6347 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6348 # address in IA_PD (temporary)
6349 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')
6351 print('### ip -6 address show dev veth98-peer scope global')
6352 output
= check_output('ip -6 address show dev veth98-peer scope global')
6354 # NDisc address (Token=static)
6355 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6356 # NDisc address (Token=eui64)
6357 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6358 # NDisc address (temporary)
6359 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')
6361 print('### ip -6 route show type unreachable')
6362 output
= check_output('ip -6 route show type unreachable')
6364 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6366 print('### ip -6 route show dev veth99')
6367 output
= check_output('ip -6 route show dev veth99')
6369 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6371 print('### ip -6 route show dev test1')
6372 output
= check_output('ip -6 route show dev test1')
6374 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6376 print('### ip -6 route show dev dummy98')
6377 output
= check_output('ip -6 route show dev dummy98')
6379 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6381 print('### ip -6 route show dev dummy99')
6382 output
= check_output('ip -6 route show dev dummy99')
6384 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6386 print('### ip -6 route show dev veth97')
6387 output
= check_output('ip -6 route show dev veth97')
6389 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6391 print('### ip -6 route show dev veth97-peer')
6392 output
= check_output('ip -6 route show dev veth97-peer')
6394 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6396 print('### ip -6 route show dev veth98')
6397 output
= check_output('ip -6 route show dev veth98')
6399 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6401 print('### ip -6 route show dev veth98-peer')
6402 output
= check_output('ip -6 route show dev veth98-peer')
6404 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6406 # Test case for a downstream which appears later
6407 check_output('ip link add dummy97 type dummy')
6408 self
.wait_online(['dummy97:routable'])
6410 print('### ip -6 address show dev dummy97 scope global')
6411 output
= check_output('ip -6 address show dev dummy97 scope global')
6413 # address in IA_PD (Token=static)
6414 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6415 # address in IA_PD (temporary)
6416 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')
6418 print('### ip -6 route show dev dummy97')
6419 output
= check_output('ip -6 route show dev dummy97')
6421 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6423 # Test case for reconfigure
6424 networkctl_reconfigure('dummy98', 'dummy99')
6425 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6427 print('### ip -6 address show dev dummy98 scope global')
6428 output
= check_output('ip -6 address show dev dummy98 scope global')
6430 # address in IA_PD (Token=static)
6431 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6432 # address in IA_PD (temporary)
6433 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')
6435 print('### ip -6 address show dev dummy99 scope global')
6436 output
= check_output('ip -6 address show dev dummy99 scope global')
6439 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6441 print('### ip -6 route show dev dummy98')
6442 output
= check_output('ip -6 route show dev dummy98')
6444 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6446 print('### ip -6 route show dev dummy99')
6447 output
= check_output('ip -6 route show dev dummy99')
6449 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6451 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6453 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6454 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6455 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6456 self
.check_nftset('ifindex', 'dummy98')
6458 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6460 def verify_dhcp4_6rd(self
, tunnel_name
):
6461 print('### ip -4 address show dev veth-peer scope global')
6462 output
= check_output('ip -4 address show dev veth-peer scope global')
6464 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6468 # dummy97: 0x01 (The link will appear later)
6470 # dummy99: auto -> 0x0[23] (No address assignment)
6471 # 6rd-XXX: auto -> 0x0[23]
6476 print('### ip -4 address show dev veth99 scope global')
6477 output
= check_output('ip -4 address show dev veth99 scope global')
6479 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6481 print('### ip -6 address show dev veth99 scope global')
6482 output
= check_output('ip -6 address show dev veth99 scope global')
6484 # address in IA_PD (Token=static)
6485 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6486 # address in IA_PD (Token=eui64)
6487 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6488 # address in IA_PD (temporary)
6489 # Note that the temporary addresses may appear after the link enters configured state
6490 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')
6492 print('### ip -6 address show dev test1 scope global')
6493 output
= check_output('ip -6 address show dev test1 scope global')
6495 # address in IA_PD (Token=static)
6496 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6497 # address in IA_PD (temporary)
6498 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')
6500 print('### ip -6 address show dev dummy98 scope global')
6501 output
= check_output('ip -6 address show dev dummy98 scope global')
6503 # address in IA_PD (Token=static)
6504 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6505 # address in IA_PD (temporary)
6506 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')
6508 print('### ip -6 address show dev dummy99 scope global')
6509 output
= check_output('ip -6 address show dev dummy99 scope global')
6512 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6514 print('### ip -6 address show dev veth97 scope global')
6515 output
= check_output('ip -6 address show dev veth97 scope global')
6517 # address in IA_PD (Token=static)
6518 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6519 # address in IA_PD (Token=eui64)
6520 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6521 # address in IA_PD (temporary)
6522 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')
6524 print('### ip -6 address show dev veth97-peer scope global')
6525 output
= check_output('ip -6 address show dev veth97-peer scope global')
6527 # NDisc address (Token=static)
6528 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6529 # NDisc address (Token=eui64)
6530 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6531 # NDisc address (temporary)
6532 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')
6534 print('### ip -6 address show dev veth98 scope global')
6535 output
= check_output('ip -6 address show dev veth98 scope global')
6537 # address in IA_PD (Token=static)
6538 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6539 # address in IA_PD (Token=eui64)
6540 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6541 # address in IA_PD (temporary)
6542 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')
6544 print('### ip -6 address show dev veth98-peer scope global')
6545 output
= check_output('ip -6 address show dev veth98-peer scope global')
6547 # NDisc address (Token=static)
6548 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6549 # NDisc address (Token=eui64)
6550 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6551 # NDisc address (temporary)
6552 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')
6554 print('### ip -6 route show type unreachable')
6555 output
= check_output('ip -6 route show type unreachable')
6557 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6559 print('### ip -6 route show dev veth99')
6560 output
= check_output('ip -6 route show dev veth99')
6562 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6564 print('### ip -6 route show dev test1')
6565 output
= check_output('ip -6 route show dev test1')
6567 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6569 print('### ip -6 route show dev dummy98')
6570 output
= check_output('ip -6 route show dev dummy98')
6572 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6574 print('### ip -6 route show dev dummy99')
6575 output
= check_output('ip -6 route show dev dummy99')
6577 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6579 print('### ip -6 route show dev veth97')
6580 output
= check_output('ip -6 route show dev veth97')
6582 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6584 print('### ip -6 route show dev veth97-peer')
6585 output
= check_output('ip -6 route show dev veth97-peer')
6587 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6589 print('### ip -6 route show dev veth98')
6590 output
= check_output('ip -6 route show dev veth98')
6592 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6594 print('### ip -6 route show dev veth98-peer')
6595 output
= check_output('ip -6 route show dev veth98-peer')
6597 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6599 print('### ip -6 address show dev dummy97 scope global')
6600 output
= check_output('ip -6 address show dev dummy97 scope global')
6602 # address in IA_PD (Token=static)
6603 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6604 # address in IA_PD (temporary)
6605 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')
6607 print('### ip -6 route show dev dummy97')
6608 output
= check_output('ip -6 route show dev dummy97')
6610 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6612 print(f
'### ip -d link show dev {tunnel_name}')
6613 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6615 self
.assertIn('link/sit 10.100.100.', output
)
6616 self
.assertIn('local 10.100.100.', output
)
6617 self
.assertIn('ttl 64', output
)
6618 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6619 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6621 print(f
'### ip -6 address show dev {tunnel_name}')
6622 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6624 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')
6625 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6627 print(f
'### ip -6 route show dev {tunnel_name}')
6628 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6630 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6631 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6633 print('### ip -6 route show default')
6634 output
= check_output('ip -6 route show default')
6636 self
.assertIn('default', output
)
6637 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6639 def test_dhcp4_6rd(self
):
6640 def get_dhcp_6rd_prefix(link
):
6641 description
= get_link_description(link
)
6643 self
.assertIn('DHCPv4Client', description
.keys())
6644 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6646 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6647 self
.assertIn('Prefix', prefixInfo
.keys())
6648 self
.assertIn('PrefixLength', prefixInfo
.keys())
6649 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6650 self
.assertIn('BorderRouters', prefixInfo
.keys())
6654 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6655 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6656 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6657 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6658 '25-dhcp-pd-downstream-dummy97.network',
6659 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6660 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6661 '80-6rd-tunnel.network')
6664 self
.wait_online(['veth-peer:routable'])
6667 # 6rd-prefix: 2001:db8::/32
6668 # br-addresss: 10.0.0.1
6670 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',
6671 ipv4_range
='10.100.100.100,10.100.100.200',
6672 ipv4_router
='10.0.0.1')
6673 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6674 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6676 # Check the DBus interface for assigned prefix information
6677 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6679 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6680 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6681 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6682 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6684 # Test case for a downstream which appears later
6685 check_output('ip link add dummy97 type dummy')
6686 self
.wait_online(['dummy97:routable'])
6690 for name
in os
.listdir('/sys/class/net/'):
6691 if name
.startswith('6rd-'):
6695 self
.wait_online([f
'{tunnel_name}:routable'])
6697 self
.verify_dhcp4_6rd(tunnel_name
)
6699 # Test case for reconfigure
6700 networkctl_reconfigure('dummy98', 'dummy99')
6701 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6703 self
.verify_dhcp4_6rd(tunnel_name
)
6705 print('Wait for the DHCP lease to be renewed/rebind')
6708 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6709 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6711 self
.verify_dhcp4_6rd(tunnel_name
)
6713 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
6721 def test_ipv6_route_prefix(self
):
6722 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
6723 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6726 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6728 output
= check_output('ip address show dev veth-peer')
6730 self
.assertIn('inet6 2001:db8:0:1:', output
)
6731 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6732 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6734 output
= check_output('ip -6 route show dev veth-peer')
6736 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6737 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6738 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
6739 self
.assertIn('2001:db0:fff::/64 via ', output
)
6740 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6741 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
6743 output
= check_output('ip address show dev veth99')
6745 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6746 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
6747 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
6748 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6750 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6752 self
.assertRegex(output
, '2001:db8:1:1::2')
6754 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6756 self
.assertIn('example.com', output
)
6758 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6761 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth-peer', env
=env
)
6765 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
6767 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
6768 self
.assertEqual(prefix
, '64:ff9b::')
6770 prefix_length
= pref64
['PrefixLength']
6771 self
.assertEqual(prefix_length
, 96)
6773 def test_ipv6_route_prefix_deny_list(self
):
6774 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
6775 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6778 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6780 output
= check_output('ip address show dev veth-peer')
6782 self
.assertIn('inet6 2001:db8:0:1:', output
)
6783 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6785 output
= check_output('ip -6 route show dev veth-peer')
6787 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6788 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6789 self
.assertIn('2001:db0:fff::/64 via ', output
)
6790 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6792 output
= check_output('ip address show dev veth99')
6794 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6795 self
.assertIn('inet6 2001:db8:0:2:', output
)
6797 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6799 self
.assertRegex(output
, '2001:db8:1:1::2')
6801 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6803 self
.assertIn('example.com', output
)
6805 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
6813 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
6819 self
.wait_online(['dummy98:routable'])
6820 self
.check_link_attr('dummy98', 'mtu', mtu
)
6821 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6823 # test normal restart
6825 self
.wait_online(['dummy98:routable'])
6826 self
.check_link_attr('dummy98', 'mtu', mtu
)
6827 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6830 self
.reset_check_mtu(mtu
, ipv6_mtu
)
6832 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
6833 ''' test setting mtu/ipv6_mtu with interface already up '''
6836 # note - changing the device mtu resets the ipv6 mtu
6837 check_output('ip link set up mtu 1501 dev dummy98')
6838 check_output('ip link set up mtu 1500 dev dummy98')
6839 self
.check_link_attr('dummy98', 'mtu', '1500')
6840 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
6842 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
6844 def test_mtu_network(self
):
6845 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
6846 self
.check_mtu('1600')
6848 def test_mtu_netdev(self
):
6849 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
6850 # note - MTU set by .netdev happens ONLY at device creation!
6851 self
.check_mtu('1600', reset
=False)
6853 def test_mtu_link(self
):
6854 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
6855 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
6856 self
.check_mtu('1600', reset
=False)
6858 def test_ipv6_mtu(self
):
6859 ''' set ipv6 mtu without setting device mtu '''
6860 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
6861 self
.check_mtu('1500', '1400')
6863 def test_ipv6_mtu_toolarge(self
):
6864 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
6865 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6866 self
.check_mtu('1500', '1500')
6868 def test_mtu_network_ipv6_mtu(self
):
6869 ''' set ipv6 mtu and set device mtu via network file '''
6870 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
6871 self
.check_mtu('1600', '1550')
6873 def test_mtu_netdev_ipv6_mtu(self
):
6874 ''' set ipv6 mtu and set device mtu via netdev file '''
6875 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6876 self
.check_mtu('1600', '1550', reset
=False)
6878 def test_mtu_link_ipv6_mtu(self
):
6879 ''' set ipv6 mtu and set device mtu via link file '''
6880 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
6881 self
.check_mtu('1600', '1550', reset
=False)
6884 if __name__
== '__main__':
6885 parser
= argparse
.ArgumentParser()
6886 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
6887 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
6888 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
6889 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
6890 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
6891 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
6892 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
6893 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
6894 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
6895 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
6896 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
6897 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
6898 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
6899 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
6900 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
6901 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
6902 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
)
6903 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
6906 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
6907 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
6908 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
6909 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
6910 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
6911 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
6912 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6913 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
6914 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
6915 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
6916 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
6917 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6918 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
6921 networkd_bin
= ns
.networkd_bin
6923 resolved_bin
= ns
.resolved_bin
6924 if ns
.timesyncd_bin
:
6925 timesyncd_bin
= ns
.timesyncd_bin
6927 udevd_bin
= ns
.udevd_bin
6928 if ns
.wait_online_bin
:
6929 wait_online_bin
= ns
.wait_online_bin
6930 if ns
.networkctl_bin
:
6931 networkctl_bin
= ns
.networkctl_bin
6932 if ns
.resolvectl_bin
:
6933 resolvectl_bin
= ns
.resolvectl_bin
6934 if ns
.timedatectl_bin
:
6935 timedatectl_bin
= ns
.timedatectl_bin
6937 udevadm_bin
= ns
.udevadm_bin
6940 systemd_source_dir
= ns
.source_dir
6942 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
6943 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
6944 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
6946 use_valgrind
= ns
.use_valgrind
6947 enable_debug
= ns
.enable_debug
6948 asan_options
= ns
.asan_options
6949 lsan_options
= ns
.lsan_options
6950 ubsan_options
= ns
.ubsan_options
6951 with_coverage
= ns
.with_coverage
6954 # Do not forget the trailing space.
6955 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
6957 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
6958 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
6959 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
6960 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
6961 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
6964 env
.update({'ASAN_OPTIONS': asan_options
})
6966 env
.update({'LSAN_OPTIONS': lsan_options
})
6968 env
.update({'UBSAN_OPTIONS': ubsan_options
})
6970 env
.update({'SYSTEMD_MEMPOOL': '0'})
6972 wait_online_env
= env
.copy()
6974 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
6976 sys
.argv
[1:] = unknown_args
6977 unittest
.main(verbosity
=3)