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
))
722 def start_networkd():
723 check_output('systemctl start systemd-networkd')
725 def restart_networkd(show_logs
=True):
727 invocation_id
= networkd_invocation_id()
728 check_output('systemctl restart systemd-networkd.service')
730 print(read_networkd_log(invocation_id
))
733 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
735 def networkctl_reconfigure(*links
):
736 check_output(*networkctl_cmd
, 'reconfigure', *links
, env
=env
)
738 def networkctl_reload(sleep_time
=1):
739 check_output(*networkctl_cmd
, 'reload', env
=env
)
740 # 'networkctl reload' asynchronously reconfigure links.
741 # Hence, we need to wait for a short time for link to be in configuring state.
743 time
.sleep(sleep_time
)
748 def tear_down_common():
749 # 1. stop DHCP/RA servers
755 call_quiet('rmmod netdevsim')
756 call_quiet('rmmod sch_teql')
758 # 3. remove network namespace
759 call_quiet('ip netns del ns99')
769 clear_network_units()
770 clear_networkd_conf_dropins()
775 flush_routing_policy_rules()
779 rm_rf(networkd_ci_temp_dir
)
780 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
782 clear_network_units()
783 clear_networkd_conf_dropins()
786 setup_systemd_udev_rules()
787 copy_udev_rule('00-debug-net.rules')
791 save_existing_links()
793 save_routing_policy_rules()
796 create_service_dropin('systemd-networkd', networkd_bin
,
799 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
801 'StartLimitIntervalSec=0'])
802 create_service_dropin('systemd-resolved', resolved_bin
)
803 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
805 # TODO: also run udevd with sanitizers, valgrind, or coverage
806 #create_service_dropin('systemd-udevd', udevd_bin,
807 # f'{udevadm_bin} control --reload --timeout 0')
809 'systemd-udevd.service',
813 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
817 'systemd-networkd.socket',
820 'StartLimitIntervalSec=0',
824 check_output('systemctl daemon-reload')
825 print(check_output('systemctl cat systemd-networkd.service'))
826 print(check_output('systemctl cat systemd-resolved.service'))
827 print(check_output('systemctl cat systemd-timesyncd.service'))
828 print(check_output('systemctl cat systemd-udevd.service'))
829 check_output('systemctl restart systemd-resolved.service')
830 check_output('systemctl restart systemd-timesyncd.service')
831 check_output('systemctl restart systemd-udevd.service')
833 def tearDownModule():
834 rm_rf(networkd_ci_temp_dir
)
836 clear_network_units()
837 clear_networkd_conf_dropins()
841 rm_rf('/run/systemd/system/systemd-networkd.service.d')
842 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
843 rm_rf('/run/systemd/system/systemd-resolved.service.d')
844 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
845 rm_rf('/run/systemd/system/systemd-udevd.service.d')
846 check_output('systemctl daemon-reload')
847 check_output('systemctl restart systemd-udevd.service')
848 restore_active_units()
851 # pylint: disable=no-member
853 def check_link_exists(self
, link
, expected
=True):
855 self
.assertTrue(link_exists(link
))
857 self
.assertFalse(link_exists(link
))
859 def check_link_attr(self
, *args
):
860 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
862 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
863 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
864 if allow_enoent
and not os
.path
.exists(path
):
866 with
open(path
, encoding
='utf-8') as f
:
867 self
.assertEqual(f
.readline().strip(), expected
)
869 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
870 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
872 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
873 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
875 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
876 def links_exist(*links
):
878 if not link_exists(link
):
882 for iteration
in range(timeout
+ 1):
886 if links_exist(*links
):
889 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
892 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
893 # wait for the interface is activated.
894 invocation_id
= check_output('systemctl show systemd-networkd -p InvocationID --value')
895 needle
= f
'{link}: Bringing link {state}'
897 for iteration
in range(timeout
+ 1):
900 if not link_exists(link
):
902 output
= check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id
)
903 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
906 self
.fail(f
'Timed out waiting for {link} activated.')
909 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
910 """Wait for the link to reach the specified operstate and/or setup state.
912 Specify None or '' for either operstate or setup_state to ignore that state.
913 This will recheck until the state conditions are met or the timeout expires.
915 If the link successfully matches the requested state, this returns True.
916 If this times out waiting for the link to match, the behavior depends on the
917 'fail_assert' parameter; if True, this causes a test assertion failure,
918 otherwise this returns False. The default is to cause assertion failure.
920 Note that this function matches on *exactly* the given operstate and setup_state.
921 To wait for a link to reach *or exceed* a given operstate, use wait_online().
928 for secs
in range(setup_timeout
+ 1):
931 if not link_exists(link
):
933 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', link
, env
=env
)
934 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
938 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
941 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
942 """Wait for the links to reach the specified operstate and/or setup state.
944 This is similar to wait_operstate() but can be used for multiple links,
945 and it also calls systemd-networkd-wait-online to wait for the given operstate.
946 The operstate should be specified in the link name, like 'eth0:degraded'.
947 If just a link name is provided, wait-online's default operstate to wait for is degraded.
949 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
950 'setup_timeout' controls the per-link timeout waiting for the setup_state.
952 Set 'bool_any' to True to wait for any (instead of all) of the given links.
953 If this is set, no setup_state checks are done.
955 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
956 This is applied only for the operational state 'degraded' or above.
958 Note that this function waits for the links to reach *or exceed* the given operstate.
959 However, the setup_state, if specified, must be matched *exactly*.
961 This returns if the links reached the requested operstate/setup_state; otherwise it
962 raises CalledProcessError or fails test assertion.
964 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
972 check_output(*args
, env
=wait_online_env
)
973 except subprocess
.CalledProcessError
:
974 # show detailed status on failure
975 for link
in links_with_operstate
:
976 name
= link
.split(':')[0]
977 if link_exists(name
):
978 call(*networkctl_cmd
, '-n', '0', 'status', name
, env
=env
)
980 if not bool_any
and setup_state
:
981 for link
in links_with_operstate
:
982 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
984 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
985 for i
in range(timeout_sec
):
988 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
989 if re
.search(address_regex
, output
) and 'tentative' not in output
:
992 self
.assertRegex(output
, address_regex
)
994 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
995 for i
in range(timeout_sec
):
998 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
999 if not re
.search(address_regex
, output
):
1002 self
.assertNotRegex(output
, address_regex
)
1004 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1005 for i
in range(timeout_sec
):
1008 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1009 if re
.search(route_regex
, output
):
1012 self
.assertRegex(output
, route_regex
)
1014 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1015 if not shutil
.which('selinuxenabled'):
1016 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1017 elif call_quiet('selinuxenabled') != 0:
1018 print('## Checking NetLabel skipped: SELinux disabled.')
1019 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1020 print('## Checking NetLabel skipped: netlabelctl command not found.')
1022 output
= check_output('netlabelctl unlbl list')
1024 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1026 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1027 if not shutil
.which('nft'):
1028 print('## Setting up NFT sets skipped: nft command not found.')
1030 if call(f
'nft add table inet sd_test') != 0:
1031 print('## Setting up NFT table failed.')
1033 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1034 print('## Setting up NFT sets failed.')
1037 def teardown_nftset(self
, *filters
):
1038 if not shutil
.which('nft'):
1039 print('## Tearing down NFT sets skipped: nft command not found.')
1041 for filter_name
in filters
:
1042 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1043 print('## Tearing down NFT sets failed.')
1045 if call(f
'nft delete table inet sd_test') != 0:
1046 print('## Tearing down NFT table failed.')
1049 def check_nftset(self
, filter_name
, contents
):
1050 if not shutil
.which('nft'):
1051 print('## Checking NFT sets skipped: nft command not found.')
1053 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1055 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1057 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1065 @expectedFailureIfAlternativeNameIsNotAvailable()
1066 def test_altname(self
):
1067 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1069 self
.wait_online(['dummy98:degraded'])
1071 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1072 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1074 @expectedFailureIfAlternativeNameIsNotAvailable()
1075 def test_rename_to_altname(self
):
1076 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1077 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1079 self
.wait_online(['dummyalt:degraded'])
1081 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummyalt', env
=env
)
1082 self
.assertIn('hogehogehogehogehogehoge', output
)
1083 self
.assertNotIn('dummy98', output
)
1085 def test_reconfigure(self
):
1086 copy_network_unit('25-address-static.network', '12-dummy.netdev')
1088 self
.wait_online(['dummy98:routable'])
1090 output
= check_output('ip -4 address show dev dummy98')
1092 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1093 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1094 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1096 check_output('ip address del 10.1.2.3/16 dev dummy98')
1097 check_output('ip address del 10.1.2.4/16 dev dummy98')
1098 check_output('ip address del 10.2.2.4/16 dev dummy98')
1100 networkctl_reconfigure('dummy98')
1101 self
.wait_online(['dummy98:routable'])
1103 output
= check_output('ip -4 address show dev dummy98')
1105 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1106 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1107 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1109 remove_network_unit('25-address-static.network')
1112 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1114 output
= check_output('ip -4 address show dev dummy98')
1116 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1117 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1118 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1120 copy_network_unit('25-address-static.network')
1122 self
.wait_online(['dummy98:routable'])
1124 output
= check_output('ip -4 address show dev dummy98')
1126 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1127 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1128 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1130 def test_renew(self
):
1132 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
1133 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
1135 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1136 self
.assertIn('Gateway: 192.168.5.3', output
)
1137 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1138 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1140 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1143 output
= check_output(*networkctl_cmd
, '--lines=0', '--stats', '--all', '--full', '--json=short', 'status')
1146 for verb
in ['renew', 'forcerenew']:
1147 call_check(*networkctl_cmd
, verb
, 'veth99')
1149 call_check(*networkctl_cmd
, verb
, 'veth99', 'veth99', 'veth99')
1152 def test_up_down(self
):
1153 copy_network_unit('25-address-static.network', '12-dummy.netdev')
1155 self
.wait_online(['dummy98:routable'])
1157 call_check(*networkctl_cmd
, 'down', 'dummy98')
1158 self
.wait_online(['dummy98:off'])
1159 call_check(*networkctl_cmd
, 'up', 'dummy98')
1160 self
.wait_online(['dummy98:routable'])
1161 call_check(*networkctl_cmd
, 'down', 'dummy98', 'dummy98', 'dummy98')
1162 self
.wait_online(['dummy98:off'])
1163 call_check(*networkctl_cmd
, 'up', 'dummy98', 'dummy98', 'dummy98')
1164 self
.wait_online(['dummy98:routable'])
1166 def test_reload(self
):
1169 copy_network_unit('11-dummy.netdev')
1171 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1173 copy_network_unit('11-dummy.network')
1175 self
.wait_online(['test1:degraded'])
1177 remove_network_unit('11-dummy.network')
1179 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1181 remove_network_unit('11-dummy.netdev')
1183 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1185 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1187 self
.wait_operstate('test1', 'degraded')
1189 def test_glob(self
):
1190 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1193 self
.wait_online(['test1:degraded'])
1195 output
= check_output(*networkctl_cmd
, 'list', env
=env
)
1196 self
.assertRegex(output
, '1 lo ')
1197 self
.assertRegex(output
, 'test1')
1199 output
= check_output(*networkctl_cmd
, 'list', 'test1', env
=env
)
1200 self
.assertNotRegex(output
, '1 lo ')
1201 self
.assertRegex(output
, 'test1')
1203 output
= check_output(*networkctl_cmd
, 'list', 'te*', env
=env
)
1204 self
.assertNotRegex(output
, '1 lo ')
1205 self
.assertRegex(output
, 'test1')
1207 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'te*', env
=env
)
1208 self
.assertNotRegex(output
, '1: lo ')
1209 self
.assertRegex(output
, 'test1')
1211 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'tes[a-z][0-9]', env
=env
)
1212 self
.assertNotRegex(output
, '1: lo ')
1213 self
.assertRegex(output
, 'test1')
1216 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1219 self
.wait_online(['test1:degraded'])
1221 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1222 self
.assertRegex(output
, 'MTU: 1600')
1224 def test_type(self
):
1225 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1227 self
.wait_online(['test1:degraded'])
1229 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1231 self
.assertRegex(output
, 'Type: ether')
1233 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1235 self
.assertRegex(output
, 'Type: loopback')
1237 def test_udev_link_file(self
):
1238 copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link')
1240 self
.wait_online(['test1:degraded'])
1242 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1244 self
.assertRegex(output
, r
'Link File: /run/systemd/network/25-default.link')
1245 self
.assertRegex(output
, r
'Network File: /run/systemd/network/11-dummy.network')
1247 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1248 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1249 # Let's reprocess the interface and drop the property.
1250 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1251 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
1253 self
.assertRegex(output
, r
'Link File: n/a')
1254 self
.assertRegex(output
, r
'Network File: n/a')
1256 def test_delete_links(self
):
1257 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1258 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1261 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
1263 check_output(*networkctl_cmd
, 'delete', 'test1', 'veth99', env
=env
)
1264 self
.check_link_exists('test1', expected
=False)
1265 self
.check_link_exists('veth99', expected
=False)
1266 self
.check_link_exists('veth-peer', expected
=False)
1268 def test_label(self
):
1269 call_check(*networkctl_cmd
, 'label')
1271 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1279 @expectedFailureIfAlternativeNameIsNotAvailable()
1280 def test_match(self
):
1281 copy_network_unit('12-dummy-mac.netdev',
1282 '12-dummy-match-mac-01.network',
1283 '12-dummy-match-mac-02.network',
1284 '12-dummy-match-renamed.network',
1285 '12-dummy-match-altname.network',
1286 '12-dummy-altname.link')
1289 self
.wait_online(['dummy98:routable'])
1290 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1291 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1292 output
= check_output('ip -4 address show dev dummy98')
1293 self
.assertIn('10.0.0.1/16', output
)
1295 check_output('ip link set dev dummy98 down')
1296 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1298 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1299 self
.wait_online(['dummy98:routable'])
1300 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1301 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1303 check_output('ip link set dev dummy98 down')
1304 check_output('ip link set dev dummy98 name dummy98-1')
1306 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1307 self
.wait_online(['dummy98-1:routable'])
1308 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-1', env
=env
)
1309 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1311 check_output('ip link set dev dummy98-1 down')
1312 check_output('ip link set dev dummy98-1 name dummy98-2')
1313 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1315 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1316 self
.wait_online(['dummy98-2:routable'])
1317 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98-2', env
=env
)
1318 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1320 def test_match_udev_property(self
):
1321 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1323 self
.wait_online(['dummy98:routable'])
1325 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
1327 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1329 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1337 def test_wait_online_any(self
):
1338 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1341 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
1343 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1344 self
.wait_operstate('test1', 'degraded')
1346 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1354 def test_dropin_and_name_conflict(self
):
1355 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1358 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
1360 output
= check_output('ip link show dropin-test')
1362 self
.assertRegex(output
, '00:50:56:c0:00:28')
1364 @expectedFailureIfModuleIsNotAvailable('bareudp')
1365 def test_bareudp(self
):
1366 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1369 self
.wait_online(['bareudp99:degraded'])
1371 output
= check_output('ip -d link show bareudp99')
1373 self
.assertRegex(output
, 'dstport 1000 ')
1374 self
.assertRegex(output
, 'ethertype ip ')
1376 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1377 def test_batadv(self
):
1378 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1381 self
.wait_online(['batadv99:degraded'])
1383 output
= check_output('ip -d link show batadv99')
1385 self
.assertRegex(output
, 'batadv')
1387 def test_bridge(self
):
1388 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1391 self
.wait_online(['bridge99:no-carrier'])
1393 tick
= os
.sysconf('SC_CLK_TCK')
1394 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1395 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1396 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1397 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1398 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1399 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1400 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1401 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1402 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1404 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
1406 self
.assertRegex(output
, 'Priority: 9')
1407 self
.assertRegex(output
, 'STP: yes')
1408 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1410 output
= check_output('ip -d link show bridge99')
1412 self
.assertIn('vlan_filtering 1 ', output
)
1413 self
.assertIn('vlan_protocol 802.1ad ', output
)
1414 self
.assertIn('vlan_default_pvid 9 ', output
)
1416 def test_bond(self
):
1417 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1420 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1422 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1423 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1424 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1425 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1426 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1427 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1428 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1429 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1430 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1431 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1432 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1434 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1435 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1437 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond99', env
=env
)
1439 self
.assertIn('Mode: 802.3ad', output
)
1440 self
.assertIn('Miimon: 1s', output
)
1441 self
.assertIn('Updelay: 2s', output
)
1442 self
.assertIn('Downdelay: 2s', output
)
1444 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bond98', env
=env
)
1446 self
.assertIn('Mode: balance-tlb', output
)
1448 def test_vlan(self
):
1449 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1450 '21-vlan.network', '21-vlan-test1.network')
1453 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1455 output
= check_output('ip -d link show test1')
1457 self
.assertRegex(output
, ' mtu 2000 ')
1459 output
= check_output('ip -d link show vlan99')
1461 self
.assertIn(' mtu 2000 ', output
)
1462 self
.assertIn('REORDER_HDR', output
)
1463 self
.assertIn('LOOSE_BINDING', output
)
1464 self
.assertIn('GVRP', output
)
1465 self
.assertIn('MVRP', output
)
1466 self
.assertIn(' id 99 ', output
)
1467 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1468 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1470 output
= check_output('ip -4 address show dev test1')
1472 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1473 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1475 output
= check_output('ip -4 address show dev vlan99')
1477 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1479 def test_vlan_on_bond(self
):
1480 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1481 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1483 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1484 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1486 self
.wait_online(['bond99:off'])
1487 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1489 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1490 # that the issue is fixed by the commit, let's allow to match both string.
1491 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1495 if log_re
.search(read_networkd_log()):
1500 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1502 self
.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
1504 def test_macvtap(self
):
1506 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1512 print(f
'### test_macvtap(mode={mode})')
1513 with self
.subTest(mode
=mode
):
1514 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1515 '11-dummy.netdev', '25-macvtap.network')
1516 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1517 f
.write('[MACVTAP]\nMode=' + mode
)
1520 self
.wait_online(['macvtap99:degraded',
1521 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1523 output
= check_output('ip -d link show macvtap99')
1525 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1527 def test_macvlan(self
):
1529 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1535 print(f
'### test_macvlan(mode={mode})')
1536 with self
.subTest(mode
=mode
):
1537 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1538 '11-dummy.netdev', '25-macvlan.network')
1539 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1540 f
.write('[MACVLAN]\nMode=' + mode
)
1543 self
.wait_online(['macvlan99:degraded',
1544 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1546 output
= check_output('ip -d link show test1')
1548 self
.assertRegex(output
, ' mtu 2000 ')
1550 output
= check_output('ip -d link show macvlan99')
1552 self
.assertRegex(output
, ' mtu 2000 ')
1553 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1555 remove_link('test1')
1558 check_output("ip link add test1 type dummy")
1559 self
.wait_online(['macvlan99:degraded',
1560 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1562 output
= check_output('ip -d link show test1')
1564 self
.assertRegex(output
, ' mtu 2000 ')
1566 output
= check_output('ip -d link show macvlan99')
1568 self
.assertRegex(output
, ' mtu 2000 ')
1569 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1571 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1572 def test_ipvlan(self
):
1574 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1580 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1581 with self
.subTest(mode
=mode
, flag
=flag
):
1582 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1583 '11-dummy.netdev', '25-ipvlan.network')
1584 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1585 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1588 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1590 output
= check_output('ip -d link show ipvlan99')
1592 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1594 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1595 def test_ipvtap(self
):
1597 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1603 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1604 with self
.subTest(mode
=mode
, flag
=flag
):
1605 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1606 '11-dummy.netdev', '25-ipvtap.network')
1607 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1608 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1611 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1613 output
= check_output('ip -d link show ipvtap99')
1615 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1617 def test_veth(self
):
1618 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1619 '25-veth-mtu.netdev')
1622 self
.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1624 output
= check_output('ip -d link show veth99')
1626 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1627 output
= check_output('ip -d link show veth-peer')
1629 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1631 output
= check_output('ip -d link show veth-mtu')
1633 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1634 self
.assertRegex(output
, 'mtu 1800')
1635 output
= check_output('ip -d link show veth-mtu-peer')
1637 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1638 self
.assertRegex(output
, 'mtu 1800')
1640 def test_tuntap(self
):
1641 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1644 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'])
1646 pid
= networkd_pid()
1647 name
= psutil
.Process(pid
).name()[:15]
1649 output
= check_output('ip -d tuntap show')
1651 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1652 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1654 output
= check_output('ip -d link show testtun99')
1656 # Old ip command does not support IFF_ flags
1657 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1658 self
.assertIn('UP,LOWER_UP', output
)
1660 output
= check_output('ip -d link show testtap99')
1662 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1663 self
.assertIn('UP,LOWER_UP', output
)
1665 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1668 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state
='unmanaged')
1670 pid
= networkd_pid()
1671 name
= psutil
.Process(pid
).name()[:15]
1673 output
= check_output('ip -d tuntap show')
1675 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1676 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1678 output
= check_output('ip -d link show testtun99')
1680 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1681 self
.assertIn('UP,LOWER_UP', output
)
1683 output
= check_output('ip -d link show testtap99')
1685 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1686 self
.assertIn('UP,LOWER_UP', output
)
1688 clear_network_units()
1690 self
.wait_online(['testtun99:off', 'testtap99:off'], setup_state
='unmanaged')
1692 output
= check_output('ip -d tuntap show')
1694 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1695 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1700 output
= check_output('ip -d link show testtun99')
1702 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1703 if 'NO-CARRIER' in output
:
1711 output
= check_output('ip -d link show testtap99')
1713 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1714 if 'NO-CARRIER' in output
:
1719 @expectedFailureIfModuleIsNotAvailable('vrf')
1721 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1724 self
.wait_online(['vrf99:carrier'])
1726 @expectedFailureIfModuleIsNotAvailable('vcan')
1727 def test_vcan(self
):
1728 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network')
1731 self
.wait_online(['vcan99:carrier'])
1733 @expectedFailureIfModuleIsNotAvailable('vxcan')
1734 def test_vxcan(self
):
1735 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1738 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1740 @expectedFailureIfModuleIsNotAvailable('wireguard')
1741 def test_wireguard(self
):
1742 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1743 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1744 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1745 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1747 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1749 output
= check_output('ip -4 address show dev wg99')
1751 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1753 output
= check_output('ip -4 address show dev wg99')
1755 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1757 output
= check_output('ip -6 address show dev wg99')
1759 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1761 output
= check_output('ip -4 address show dev wg98')
1763 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1765 output
= check_output('ip -6 address show dev wg98')
1767 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1769 output
= check_output('ip -4 route show dev wg99 table 1234')
1771 self
.assertIn('192.168.26.0/24 proto static metric 123', output
)
1773 output
= check_output('ip -6 route show dev wg99 table 1234')
1775 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1777 output
= check_output('ip -6 route show dev wg98 table 1234')
1779 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1780 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1781 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1782 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1783 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1784 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1785 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1786 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1787 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1788 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1789 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1790 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1791 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1792 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1793 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1794 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1795 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1796 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1797 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1798 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1799 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1800 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1801 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1802 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1803 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1804 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1805 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1806 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1807 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1808 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1809 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1810 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1811 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1812 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1813 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1814 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1815 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1816 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1817 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1818 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1819 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1820 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1821 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1822 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1823 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1824 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1826 if shutil
.which('wg'):
1829 output
= check_output('wg show wg99 listen-port')
1830 self
.assertEqual(output
, '51820')
1831 output
= check_output('wg show wg99 fwmark')
1832 self
.assertEqual(output
, '0x4d2')
1833 output
= check_output('wg show wg99 private-key')
1834 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1835 output
= check_output('wg show wg99 allowed-ips')
1836 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1837 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1838 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1839 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1840 output
= check_output('wg show wg99 persistent-keepalive')
1841 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1842 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1843 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1844 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1845 output
= check_output('wg show wg99 endpoints')
1846 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1847 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1848 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1849 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1850 output
= check_output('wg show wg99 preshared-keys')
1851 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1852 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1853 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1854 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1856 output
= check_output('wg show wg98 private-key')
1857 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1859 output
= check_output('wg show wg97 listen-port')
1860 self
.assertEqual(output
, '51821')
1861 output
= check_output('wg show wg97 fwmark')
1862 self
.assertEqual(output
, '0x4d3')
1864 def test_geneve(self
):
1865 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1868 self
.wait_online(['geneve99:degraded'])
1870 output
= check_output('ip -d link show geneve99')
1872 self
.assertRegex(output
, '192.168.22.1')
1873 self
.assertRegex(output
, '6082')
1874 self
.assertRegex(output
, 'udpcsum')
1875 self
.assertRegex(output
, 'udp6zerocsumrx')
1877 def test_ipip_tunnel(self
):
1878 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1879 '25-ipip-tunnel.netdev', '25-tunnel.network',
1880 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1881 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1882 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1884 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1886 output
= check_output('ip -d link show ipiptun99')
1888 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1889 output
= check_output('ip -d link show ipiptun98')
1891 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1892 output
= check_output('ip -d link show ipiptun97')
1894 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1895 output
= check_output('ip -d link show ipiptun96')
1897 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1899 def test_gre_tunnel(self
):
1900 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1901 '25-gre-tunnel.netdev', '25-tunnel.network',
1902 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1903 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1904 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1906 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1908 output
= check_output('ip -d link show gretun99')
1910 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1911 self
.assertRegex(output
, 'ikey 1.2.3.103')
1912 self
.assertRegex(output
, 'okey 1.2.4.103')
1913 self
.assertRegex(output
, 'iseq')
1914 self
.assertRegex(output
, 'oseq')
1915 output
= check_output('ip -d link show gretun98')
1917 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1918 self
.assertRegex(output
, 'ikey 0.0.0.104')
1919 self
.assertRegex(output
, 'okey 0.0.0.104')
1920 self
.assertNotRegex(output
, 'iseq')
1921 self
.assertNotRegex(output
, 'oseq')
1922 output
= check_output('ip -d link show gretun97')
1924 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1925 self
.assertRegex(output
, 'ikey 0.0.0.105')
1926 self
.assertRegex(output
, 'okey 0.0.0.105')
1927 self
.assertNotRegex(output
, 'iseq')
1928 self
.assertNotRegex(output
, 'oseq')
1929 output
= check_output('ip -d link show gretun96')
1931 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1932 self
.assertRegex(output
, 'ikey 0.0.0.106')
1933 self
.assertRegex(output
, 'okey 0.0.0.106')
1934 self
.assertNotRegex(output
, 'iseq')
1935 self
.assertNotRegex(output
, 'oseq')
1937 def test_ip6gre_tunnel(self
):
1938 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
1939 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1940 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1941 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1942 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1945 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1947 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
1949 output
= check_output('ip -d link show ip6gretun99')
1951 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1952 output
= check_output('ip -d link show ip6gretun98')
1954 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1955 output
= check_output('ip -d link show ip6gretun97')
1957 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1958 output
= check_output('ip -d link show ip6gretun96')
1960 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
1962 def test_gretap_tunnel(self
):
1963 copy_network_unit('12-dummy.netdev', '25-gretap.network',
1964 '25-gretap-tunnel.netdev', '25-tunnel.network',
1965 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1967 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1969 output
= check_output('ip -d link show gretap99')
1971 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1972 self
.assertRegex(output
, 'ikey 0.0.0.106')
1973 self
.assertRegex(output
, 'okey 0.0.0.106')
1974 self
.assertRegex(output
, 'iseq')
1975 self
.assertRegex(output
, 'oseq')
1976 self
.assertIn('nopmtudisc', output
)
1977 self
.assertIn('ignore-df', output
)
1978 output
= check_output('ip -d link show gretap98')
1980 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
1981 self
.assertRegex(output
, 'ikey 0.0.0.107')
1982 self
.assertRegex(output
, 'okey 0.0.0.107')
1983 self
.assertRegex(output
, 'iseq')
1984 self
.assertRegex(output
, 'oseq')
1986 def test_ip6gretap_tunnel(self
):
1987 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
1988 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
1989 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1991 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
1993 output
= check_output('ip -d link show ip6gretap99')
1995 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1996 output
= check_output('ip -d link show ip6gretap98')
1998 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2000 def test_vti_tunnel(self
):
2001 copy_network_unit('12-dummy.netdev', '25-vti.network',
2002 '25-vti-tunnel.netdev', '25-tunnel.network',
2003 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2004 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2005 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2007 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2009 output
= check_output('ip -d link show vtitun99')
2011 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2012 output
= check_output('ip -d link show vtitun98')
2014 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2015 output
= check_output('ip -d link show vtitun97')
2017 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2018 output
= check_output('ip -d link show vtitun96')
2020 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2022 def test_vti6_tunnel(self
):
2023 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2024 '25-vti6-tunnel.netdev', '25-tunnel.network',
2025 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2026 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2028 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2030 output
= check_output('ip -d link show vti6tun99')
2032 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2033 output
= check_output('ip -d link show vti6tun98')
2035 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2036 output
= check_output('ip -d link show vti6tun97')
2038 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2040 def test_ip6tnl_tunnel(self
):
2041 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2042 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2043 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2044 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2045 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2046 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2047 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2049 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2050 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2051 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2053 output
= check_output('ip -d link show ip6tnl99')
2055 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2056 output
= check_output('ip -d link show ip6tnl98')
2058 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2059 output
= check_output('ip -d link show ip6tnl97')
2061 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2062 output
= check_output('ip -d link show ip6tnl-external')
2064 self
.assertIn('ip6tnl-external@NONE:', output
)
2065 self
.assertIn('ip6tnl external ', output
)
2066 output
= check_output('ip -d link show ip6tnl-slaac')
2068 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2070 output
= check_output('ip -6 address show veth99')
2072 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2074 output
= check_output('ip -4 route show default')
2076 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2078 def test_sit_tunnel(self
):
2079 copy_network_unit('12-dummy.netdev', '25-sit.network',
2080 '25-sit-tunnel.netdev', '25-tunnel.network',
2081 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2082 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2083 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2085 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2087 output
= check_output('ip -d link show sittun99')
2089 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2090 output
= check_output('ip -d link show sittun98')
2092 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2093 output
= check_output('ip -d link show sittun97')
2095 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2096 output
= check_output('ip -d link show sittun96')
2098 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2100 def test_isatap_tunnel(self
):
2101 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2102 '25-isatap-tunnel.netdev', '25-tunnel.network')
2104 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2106 output
= check_output('ip -d link show isataptun99')
2108 self
.assertRegex(output
, "isatap ")
2110 def test_6rd_tunnel(self
):
2111 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2112 '25-6rd-tunnel.netdev', '25-tunnel.network')
2114 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2116 output
= check_output('ip -d link show sittun99')
2118 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2120 @expectedFailureIfERSPANv0IsNotSupported()
2121 def test_erspan_tunnel_v0(self
):
2122 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2123 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2124 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2126 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2128 output
= check_output('ip -d link show erspan99')
2130 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2131 self
.assertIn('erspan_ver 0', output
)
2132 self
.assertNotIn('erspan_index 123', output
)
2133 self
.assertNotIn('erspan_dir ingress', output
)
2134 self
.assertNotIn('erspan_hwid 1f', output
)
2135 self
.assertIn('ikey 0.0.0.101', output
)
2136 self
.assertIn('iseq', output
)
2137 self
.assertIn('nopmtudisc', output
)
2138 self
.assertIn('ignore-df', output
)
2139 output
= check_output('ip -d link show erspan98')
2141 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2142 self
.assertIn('erspan_ver 0', output
)
2143 self
.assertNotIn('erspan_index 124', output
)
2144 self
.assertNotIn('erspan_dir egress', output
)
2145 self
.assertNotIn('erspan_hwid 2f', output
)
2146 self
.assertIn('ikey 0.0.0.102', output
)
2147 self
.assertIn('iseq', output
)
2149 def test_erspan_tunnel_v1(self
):
2150 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2151 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2152 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2154 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2156 output
= check_output('ip -d link show erspan99')
2158 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2159 self
.assertIn('erspan_ver 1', output
)
2160 self
.assertIn('erspan_index 123', output
)
2161 self
.assertNotIn('erspan_dir ingress', output
)
2162 self
.assertNotIn('erspan_hwid 1f', output
)
2163 self
.assertIn('ikey 0.0.0.101', output
)
2164 self
.assertIn('okey 0.0.0.101', output
)
2165 self
.assertIn('iseq', output
)
2166 self
.assertIn('oseq', output
)
2167 output
= check_output('ip -d link show erspan98')
2169 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2170 self
.assertIn('erspan_ver 1', output
)
2171 self
.assertIn('erspan_index 124', output
)
2172 self
.assertNotIn('erspan_dir egress', output
)
2173 self
.assertNotIn('erspan_hwid 2f', output
)
2174 self
.assertIn('ikey 0.0.0.102', output
)
2175 self
.assertIn('okey 0.0.0.102', output
)
2176 self
.assertIn('iseq', output
)
2177 self
.assertIn('oseq', output
)
2179 @expectedFailureIfERSPANv2IsNotSupported()
2180 def test_erspan_tunnel_v2(self
):
2181 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2182 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2183 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2185 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2187 output
= check_output('ip -d link show erspan99')
2189 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2190 self
.assertIn('erspan_ver 2', output
)
2191 self
.assertNotIn('erspan_index 123', output
)
2192 self
.assertIn('erspan_dir ingress', output
)
2193 self
.assertIn('erspan_hwid 0x1f', output
)
2194 self
.assertIn('ikey 0.0.0.101', output
)
2195 self
.assertIn('okey 0.0.0.101', output
)
2196 self
.assertIn('iseq', output
)
2197 self
.assertIn('oseq', output
)
2198 output
= check_output('ip -d link show erspan98')
2200 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2201 self
.assertIn('erspan_ver 2', output
)
2202 self
.assertNotIn('erspan_index 124', output
)
2203 self
.assertIn('erspan_dir egress', output
)
2204 self
.assertIn('erspan_hwid 0x2f', output
)
2205 self
.assertIn('ikey 0.0.0.102', output
)
2206 self
.assertIn('okey 0.0.0.102', output
)
2207 self
.assertIn('iseq', output
)
2208 self
.assertIn('oseq', output
)
2210 def test_tunnel_independent(self
):
2211 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2214 self
.wait_online(['ipiptun99:carrier'])
2216 def test_tunnel_independent_loopback(self
):
2217 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2220 self
.wait_online(['ipiptun99:carrier'])
2222 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2223 def test_xfrm(self
):
2224 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2225 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2226 '26-netdev-link-local-addressing-yes.network')
2229 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2231 output
= check_output('ip -d link show dev xfrm98')
2233 self
.assertIn('xfrm98@dummy98:', output
)
2234 self
.assertIn('xfrm if_id 0x98 ', output
)
2236 output
= check_output('ip -d link show dev xfrm99')
2238 self
.assertIn('xfrm99@lo:', output
)
2239 self
.assertIn('xfrm if_id 0x99 ', output
)
2241 @expectedFailureIfModuleIsNotAvailable('fou')
2243 # The following redundant check is necessary for CentOS CI.
2244 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2245 self
.assertTrue(is_module_available('fou'))
2247 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2248 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2249 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2252 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2254 output
= check_output('ip fou show')
2256 self
.assertRegex(output
, 'port 55555 ipproto 4')
2257 self
.assertRegex(output
, 'port 55556 ipproto 47')
2259 output
= check_output('ip -d link show ipiptun96')
2261 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2262 output
= check_output('ip -d link show sittun96')
2264 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2265 output
= check_output('ip -d link show gretun96')
2267 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2268 output
= check_output('ip -d link show gretap96')
2270 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2272 def test_vxlan(self
):
2273 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2274 '25-vxlan.netdev', '25-vxlan.network',
2275 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2276 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2277 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2278 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2281 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2282 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2284 output
= check_output('ip -d -d link show vxlan99')
2286 self
.assertIn('999', output
)
2287 self
.assertIn('5555', output
)
2288 self
.assertIn('l2miss', output
)
2289 self
.assertIn('l3miss', output
)
2290 self
.assertIn('gbp', output
)
2291 # Since [0] some of the options use slightly different names and some
2292 # options with default values are shown only if the -d(etails) setting
2294 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2295 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2296 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2297 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2298 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2299 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2301 output
= check_output('bridge fdb show dev vxlan99')
2303 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2304 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2305 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2307 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'vxlan99', env
=env
)
2309 self
.assertIn('VNI: 999', output
)
2310 self
.assertIn('Destination Port: 5555', output
)
2311 self
.assertIn('Underlying Device: test1', output
)
2313 output
= check_output('bridge fdb show dev vxlan97')
2315 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2316 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2317 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2319 output
= check_output('ip -d link show vxlan-slaac')
2321 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2323 output
= check_output('ip -6 address show veth99')
2325 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2327 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2328 def test_macsec(self
):
2329 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2330 '26-macsec.network', '12-dummy.netdev')
2333 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2335 output
= check_output('ip -d link show macsec99')
2337 self
.assertRegex(output
, 'macsec99@dummy98')
2338 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2339 self
.assertRegex(output
, 'encrypt on')
2341 output
= check_output('ip macsec show macsec99')
2343 self
.assertRegex(output
, 'encrypt on')
2344 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2345 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2346 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2347 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2348 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2349 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2350 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2351 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2352 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2353 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2354 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2356 def test_nlmon(self
):
2357 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2360 self
.wait_online(['nlmon99:carrier'])
2362 @expectedFailureIfModuleIsNotAvailable('ifb')
2364 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2367 self
.wait_online(['ifb99:degraded'])
2369 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2377 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2378 def test_l2tp_udp(self
):
2379 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2380 '25-l2tp-udp.netdev', '25-l2tp.network')
2383 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2385 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2387 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2388 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2389 self
.assertRegex(output
, "Peer tunnel 11")
2390 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2391 self
.assertRegex(output
, "UDP checksum: enabled")
2393 output
= check_output('ip l2tp show session tid 10 session_id 15')
2395 self
.assertRegex(output
, "Session 15 in tunnel 10")
2396 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2397 self
.assertRegex(output
, "interface name: l2tp-ses1")
2399 output
= check_output('ip l2tp show session tid 10 session_id 17')
2401 self
.assertRegex(output
, "Session 17 in tunnel 10")
2402 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2403 self
.assertRegex(output
, "interface name: l2tp-ses2")
2405 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2406 def test_l2tp_ip(self
):
2407 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2408 '25-l2tp-ip.netdev', '25-l2tp.network')
2411 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2413 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2415 self
.assertRegex(output
, "Tunnel 10, encap IP")
2416 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2417 self
.assertRegex(output
, "Peer tunnel 12")
2419 output
= check_output('ip l2tp show session tid 10 session_id 25')
2421 self
.assertRegex(output
, "Session 25 in tunnel 10")
2422 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2423 self
.assertRegex(output
, "interface name: l2tp-ses3")
2425 output
= check_output('ip l2tp show session tid 10 session_id 27')
2427 self
.assertRegex(output
, "Session 27 in tunnel 10")
2428 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2429 self
.assertRegex(output
, "interface name: l2tp-ses4")
2431 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2439 def verify_address_static(
2469 output
= check_output('ip address show dev dummy98')
2473 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2474 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2475 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2476 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2477 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2478 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2481 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2482 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2483 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2486 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2487 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2488 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2491 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2492 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2493 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2494 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2495 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2496 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2499 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2500 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2503 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2504 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2505 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2506 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2509 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2510 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2512 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2514 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2516 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2518 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2521 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2522 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2523 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2524 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2527 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2528 prefix16
= ip4_null_16
[:-len('.0.1')]
2529 self
.assertTrue(ip4_null_24
.endswith('.1'))
2530 prefix24
= ip4_null_24
[:-len('.1')]
2531 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2532 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2533 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2534 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2537 self
.assertNotIn('10.4.4.1', output
)
2538 self
.assertNotIn('10.5.4.1', output
)
2539 self
.assertNotIn('10.5.5.1', output
)
2540 self
.assertNotIn('10.8.2.1', output
)
2541 self
.assertNotIn('10.9.3.1', output
)
2542 self
.assertNotIn('2001:db8:0:f101::2', output
)
2543 self
.assertNotIn('2001:db8:0:f103::4', output
)
2546 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2548 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2551 def test_address_static(self
):
2552 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2554 self
.setup_nftset('addr4', 'ipv4_addr')
2555 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2556 self
.setup_nftset('ifindex', 'iface_index')
2558 self
.wait_online(['dummy98:routable'])
2562 output
= check_output('ip -4 --json address show dev dummy98')
2563 for i
in json
.loads(output
)[0]['addr_info']:
2564 if i
['label'] == 'subnet16':
2565 ip4_null_16
= i
['local']
2566 elif i
['label'] == 'subnet24':
2567 ip4_null_24
= i
['local']
2568 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2569 self
.assertTrue(ip4_null_24
.endswith('.1'))
2573 output
= check_output('ip -6 --json address show dev dummy98')
2574 for i
in json
.loads(output
)[0]['addr_info']:
2575 if i
['prefixlen'] == 73:
2576 ip6_null_73
= i
['local']
2577 elif i
['prefixlen'] == 74:
2578 ip6_null_74
= i
['local']
2579 self
.assertTrue(ip6_null_73
.endswith(':1'))
2580 self
.assertTrue(ip6_null_74
.endswith(':1'))
2582 self
.verify_address_static(
2587 broadcast2
=' brd 10.4.2.255',
2588 broadcast3
=' brd 10.4.3.63',
2589 peer1
=' peer 10.5.1.101/24',
2590 peer2
=' peer 10.5.2.101/24',
2591 peer3
='/24 brd 10.5.3.255',
2592 peer4
=' peer 2001:db8:0:f103::101/128',
2593 peer5
=' peer 2001:db8:0:f103::102/128',
2598 deprecated2
=' deprecated',
2600 deprecated4
=' deprecated',
2602 flag1
=' noprefixroute',
2604 flag3
=' noprefixroute',
2605 flag4
=' home mngtmpaddr',
2606 ip4_null_16
=ip4_null_16
,
2607 ip4_null_24
=ip4_null_24
,
2608 ip6_null_73
=ip6_null_73
,
2609 ip6_null_74
=ip6_null_74
,
2612 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2613 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2614 self
.check_nftset('ifindex', 'dummy98')
2616 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2618 copy_network_unit('25-address-static.network.d/10-override.conf')
2620 self
.wait_online(['dummy98:routable'])
2621 self
.verify_address_static(
2622 label1
='new-label1',
2624 label3
='new-label3',
2625 broadcast1
=' brd 10.4.1.255',
2627 broadcast3
=' brd 10.4.3.31',
2628 peer1
=' peer 10.5.1.102/24',
2629 peer2
='/24 brd 10.5.2.255',
2630 peer3
=' peer 10.5.3.102/24',
2631 peer4
=' peer 2001:db8:0:f103::201/128',
2633 peer6
=' peer 2001:db8:0:f103::203/128',
2636 deprecated1
=' deprecated',
2638 deprecated3
=' deprecated',
2642 flag2
=' noprefixroute',
2643 flag3
=' home mngtmpaddr',
2644 flag4
=' noprefixroute',
2645 ip4_null_16
=ip4_null_16
,
2646 ip4_null_24
=ip4_null_24
,
2647 ip6_null_73
=ip6_null_73
,
2648 ip6_null_74
=ip6_null_74
,
2651 networkctl_reconfigure('dummy98')
2652 self
.wait_online(['dummy98:routable'])
2653 self
.verify_address_static(
2654 label1
='new-label1',
2656 label3
='new-label3',
2657 broadcast1
=' brd 10.4.1.255',
2659 broadcast3
=' brd 10.4.3.31',
2660 peer1
=' peer 10.5.1.102/24',
2661 peer2
='/24 brd 10.5.2.255',
2662 peer3
=' peer 10.5.3.102/24',
2663 peer4
=' peer 2001:db8:0:f103::201/128',
2665 peer6
=' peer 2001:db8:0:f103::203/128',
2668 deprecated1
=' deprecated',
2670 deprecated3
=' deprecated',
2674 flag2
=' noprefixroute',
2675 flag3
=' home mngtmpaddr',
2676 flag4
=' noprefixroute',
2677 ip4_null_16
=ip4_null_16
,
2678 ip4_null_24
=ip4_null_24
,
2679 ip6_null_73
=ip6_null_73
,
2680 ip6_null_74
=ip6_null_74
,
2684 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2685 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2686 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2687 output
= check_output('ip address show dev dummy98')
2689 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2690 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2692 # 2. reconfigure the interface, and check the deprecated flag is set again
2693 networkctl_reconfigure('dummy98')
2694 self
.wait_online(['dummy98:routable'])
2695 self
.verify_address_static(
2696 label1
='new-label1',
2698 label3
='new-label3',
2699 broadcast1
=' brd 10.4.1.255',
2701 broadcast3
=' brd 10.4.3.31',
2702 peer1
=' peer 10.5.1.102/24',
2703 peer2
='/24 brd 10.5.2.255',
2704 peer3
=' peer 10.5.3.102/24',
2705 peer4
=' peer 2001:db8:0:f103::201/128',
2707 peer6
=' peer 2001:db8:0:f103::203/128',
2710 deprecated1
=' deprecated',
2712 deprecated3
=' deprecated',
2716 flag2
=' noprefixroute',
2717 flag3
=' home mngtmpaddr',
2718 flag4
=' noprefixroute',
2719 ip4_null_16
=ip4_null_16
,
2720 ip4_null_24
=ip4_null_24
,
2721 ip6_null_73
=ip6_null_73
,
2722 ip6_null_74
=ip6_null_74
,
2725 # test for ENOBUFS issue #17012 (with reload)
2726 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2728 self
.wait_online(['dummy98:routable'])
2729 output
= check_output('ip -4 address show dev dummy98')
2730 for i
in range(1, 254):
2731 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2733 # (with reconfigure)
2734 networkctl_reconfigure('dummy98')
2735 self
.wait_online(['dummy98:routable'])
2736 output
= check_output('ip -4 address show dev dummy98')
2737 for i
in range(1, 254):
2738 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2740 def test_address_ipv4acd(self
):
2741 check_output('ip netns add ns99')
2742 check_output('ip link add veth99 type veth peer veth-peer')
2743 check_output('ip link set veth-peer netns ns99')
2744 check_output('ip link set veth99 up')
2745 check_output('ip netns exec ns99 ip link set veth-peer up')
2746 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2748 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2750 self
.wait_online(['veth99:routable'])
2752 output
= check_output('ip -4 address show dev veth99')
2754 self
.assertNotIn('192.168.100.10/24', output
)
2755 self
.assertIn('192.168.100.11/24', output
)
2757 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2759 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2761 output
= check_output('ip -4 address show dev veth99')
2763 self
.assertNotIn('192.168.100.10/24', output
)
2764 self
.assertIn('192.168.100.11/24', output
)
2766 def test_address_peer_ipv4(self
):
2767 # test for issue #17304
2768 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2770 for trial
in range(2):
2776 self
.wait_online(['dummy98:routable'])
2778 output
= check_output('ip -4 address show dev dummy98')
2779 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2781 @expectedFailureIfModuleIsNotAvailable('vrf')
2782 def test_prefix_route(self
):
2783 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2784 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2785 '25-vrf.netdev', '25-vrf.network')
2786 for trial
in range(2):
2792 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2794 output
= check_output('ip route show table 42 dev dummy98')
2795 print('### ip route show table 42 dev dummy98')
2797 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2798 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2799 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2800 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2801 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2802 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2803 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2804 output
= check_output('ip -6 route show table 42 dev dummy98')
2805 print('### ip -6 route show table 42 dev dummy98')
2809 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2810 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2811 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2812 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2813 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2814 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2815 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2816 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2820 output
= check_output('ip route show dev test1')
2821 print('### ip route show dev test1')
2823 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2824 output
= check_output('ip route show table local dev test1')
2825 print('### ip route show table local dev test1')
2827 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2828 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2829 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2830 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2831 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2832 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2833 output
= check_output('ip -6 route show dev test1')
2834 print('### ip -6 route show dev test1')
2836 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2837 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2838 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2839 output
= check_output('ip -6 route show table local dev test1')
2840 print('### ip -6 route show table local dev test1')
2842 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2843 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2844 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2845 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2846 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2848 def test_configure_without_carrier(self
):
2849 copy_network_unit('11-dummy.netdev')
2851 self
.wait_operstate('test1', 'off', '')
2852 check_output('ip link set dev test1 up carrier off')
2854 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2856 self
.wait_online(['test1:no-carrier'])
2858 carrier_map
= {'on': '1', 'off': '0'}
2859 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2860 for carrier
in ['off', 'on', 'off']:
2861 with self
.subTest(carrier
=carrier
):
2862 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2863 check_output(f
'ip link set dev test1 carrier {carrier}')
2864 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2866 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2868 self
.assertRegex(output
, '192.168.0.15')
2869 self
.assertRegex(output
, '192.168.0.1')
2870 self
.assertRegex(output
, routable_map
[carrier
])
2872 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2873 copy_network_unit('11-dummy.netdev')
2875 self
.wait_operstate('test1', 'off', '')
2876 check_output('ip link set dev test1 up carrier off')
2878 copy_network_unit('25-test1.network')
2880 self
.wait_online(['test1:no-carrier'])
2882 carrier_map
= {'on': '1', 'off': '0'}
2883 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2884 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2885 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2886 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2887 check_output(f
'ip link set dev test1 carrier {carrier}')
2888 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2890 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
2893 self
.assertRegex(output
, '192.168.0.15')
2894 self
.assertRegex(output
, '192.168.0.1')
2896 self
.assertNotRegex(output
, '192.168.0.15')
2897 self
.assertNotRegex(output
, '192.168.0.1')
2898 self
.assertRegex(output
, routable_map
[carrier
])
2900 def test_routing_policy_rule(self
):
2901 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2903 self
.wait_online(['test1:degraded'])
2905 output
= check_output('ip rule list iif test1 priority 111')
2907 self
.assertRegex(output
, '111:')
2908 self
.assertRegex(output
, 'from 192.168.100.18')
2909 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2910 self
.assertRegex(output
, 'iif test1')
2911 self
.assertRegex(output
, 'oif test1')
2912 self
.assertRegex(output
, 'lookup 7')
2914 output
= check_output('ip rule list iif test1 priority 101')
2916 self
.assertRegex(output
, '101:')
2917 self
.assertRegex(output
, 'from all')
2918 self
.assertRegex(output
, 'iif test1')
2919 self
.assertRegex(output
, 'lookup 9')
2921 output
= check_output('ip -6 rule list iif test1 priority 100')
2923 self
.assertRegex(output
, '100:')
2924 self
.assertRegex(output
, 'from all')
2925 self
.assertRegex(output
, 'iif test1')
2926 self
.assertRegex(output
, 'lookup 8')
2928 output
= check_output('ip rule list iif test1 priority 102')
2930 self
.assertRegex(output
, '102:')
2931 self
.assertRegex(output
, 'from 0.0.0.0/8')
2932 self
.assertRegex(output
, 'iif test1')
2933 self
.assertRegex(output
, 'lookup 10')
2935 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
2938 def test_routing_policy_rule_issue_11280(self
):
2939 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
2940 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
2942 for trial
in range(3):
2943 restart_networkd(show_logs
=(trial
> 0))
2944 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
2946 output
= check_output('ip rule list table 7')
2948 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2950 output
= check_output('ip rule list table 8')
2952 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2954 def test_routing_policy_rule_reconfigure(self
):
2955 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
2957 self
.wait_online(['test1:degraded'])
2959 output
= check_output('ip rule list table 1011')
2961 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2962 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2963 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2964 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2966 output
= check_output('ip -6 rule list table 1011')
2968 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2970 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
2972 self
.wait_online(['test1:degraded'])
2974 output
= check_output('ip rule list table 1011')
2976 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
2977 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
2978 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2979 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
2981 output
= check_output('ip -6 rule list table 1011')
2983 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
2984 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
2986 call('ip rule delete priority 10111')
2987 call('ip rule delete priority 10112')
2988 call('ip rule delete priority 10113')
2989 call('ip rule delete priority 10114')
2990 call('ip -6 rule delete priority 10113')
2992 output
= check_output('ip rule list table 1011')
2994 self
.assertEqual(output
, '')
2996 output
= check_output('ip -6 rule list table 1011')
2998 self
.assertEqual(output
, '')
3000 networkctl_reconfigure('test1')
3001 self
.wait_online(['test1:degraded'])
3003 output
= check_output('ip rule list table 1011')
3005 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3006 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3007 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3008 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3010 output
= check_output('ip -6 rule list table 1011')
3012 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3014 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3015 def test_routing_policy_rule_port_range(self
):
3016 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3018 self
.wait_online(['test1:degraded'])
3020 output
= check_output('ip rule')
3022 self
.assertRegex(output
, '111')
3023 self
.assertRegex(output
, 'from 192.168.100.18')
3024 self
.assertRegex(output
, '1123-1150')
3025 self
.assertRegex(output
, '3224-3290')
3026 self
.assertRegex(output
, 'tcp')
3027 self
.assertRegex(output
, 'lookup 7')
3029 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3030 def test_routing_policy_rule_invert(self
):
3031 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3033 self
.wait_online(['test1:degraded'])
3035 output
= check_output('ip rule')
3037 self
.assertRegex(output
, '111')
3038 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3039 self
.assertRegex(output
, 'tcp')
3040 self
.assertRegex(output
, 'lookup 7')
3042 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3043 def test_routing_policy_rule_uidrange(self
):
3044 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3046 self
.wait_online(['test1:degraded'])
3048 output
= check_output('ip rule')
3050 self
.assertRegex(output
, '111')
3051 self
.assertRegex(output
, 'from 192.168.100.18')
3052 self
.assertRegex(output
, 'lookup 7')
3053 self
.assertRegex(output
, 'uidrange 100-200')
3055 def _test_route_static(self
, manage_foreign_routes
):
3056 if not manage_foreign_routes
:
3057 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3059 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3060 '25-route-static-test1.network', '11-dummy.netdev')
3062 self
.wait_online(['dummy98:routable'])
3064 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3067 print('### ip -6 route show dev dummy98')
3068 output
= check_output('ip -6 route show dev dummy98')
3070 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3071 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3072 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3074 print('### ip -6 route show default')
3075 output
= check_output('ip -6 route show default')
3077 self
.assertIn('default', output
)
3078 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3080 print('### ip -4 route show dev dummy98')
3081 output
= check_output('ip -4 route show dev dummy98')
3083 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3084 self
.assertIn('149.10.124.64 proto static scope link', output
)
3085 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3086 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3087 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3088 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3089 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3090 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3092 print('### ip -4 route show dev dummy98 default')
3093 output
= check_output('ip -4 route show dev dummy98 default')
3095 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3096 self
.assertIn('default via 149.10.124.64 proto static', output
)
3097 self
.assertIn('default proto static', output
)
3099 print('### ip -4 route show table local dev dummy98')
3100 output
= check_output('ip -4 route show table local dev dummy98')
3102 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3103 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3104 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3106 print('### ip -4 route show type blackhole')
3107 output
= check_output('ip -4 route show type blackhole')
3109 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3111 print('### ip -4 route show type unreachable')
3112 output
= check_output('ip -4 route show type unreachable')
3114 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3116 print('### ip -4 route show type prohibit')
3117 output
= check_output('ip -4 route show type prohibit')
3119 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3121 print('### ip -6 route show type blackhole')
3122 output
= check_output('ip -6 route show type blackhole')
3124 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3126 print('### ip -6 route show type unreachable')
3127 output
= check_output('ip -6 route show type unreachable')
3129 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3131 print('### ip -6 route show type prohibit')
3132 output
= check_output('ip -6 route show type prohibit')
3134 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3136 print('### ip route show 192.168.10.1')
3137 output
= check_output('ip route show 192.168.10.1')
3139 self
.assertIn('192.168.10.1 proto static', output
)
3140 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3141 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3142 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3143 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3145 print('### ip route show 192.168.10.2')
3146 output
= check_output('ip route show 192.168.10.2')
3148 # old ip command does not show IPv6 gateways...
3149 self
.assertIn('192.168.10.2 proto static', output
)
3150 self
.assertIn('nexthop', output
)
3151 self
.assertIn('dev test1 weight 20', output
)
3152 self
.assertIn('dev test1 weight 30', output
)
3153 self
.assertIn('dev dummy98 weight 10', output
)
3154 self
.assertIn('dev dummy98 weight 5', output
)
3156 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3157 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3159 # old ip command does not show 'nexthop' keyword and weight...
3160 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3161 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3162 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3163 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3164 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3166 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3169 copy_network_unit('25-address-static.network')
3171 self
.wait_online(['dummy98:routable'])
3173 # check all routes managed by Manager are removed
3174 print('### ip -4 route show type blackhole')
3175 output
= check_output('ip -4 route show type blackhole')
3177 self
.assertEqual(output
, '')
3179 print('### ip -4 route show type unreachable')
3180 output
= check_output('ip -4 route show type unreachable')
3182 self
.assertEqual(output
, '')
3184 print('### ip -4 route show type prohibit')
3185 output
= check_output('ip -4 route show type prohibit')
3187 self
.assertEqual(output
, '')
3189 print('### ip -6 route show type blackhole')
3190 output
= check_output('ip -6 route show type blackhole')
3192 self
.assertEqual(output
, '')
3194 print('### ip -6 route show type unreachable')
3195 output
= check_output('ip -6 route show type unreachable')
3197 self
.assertEqual(output
, '')
3199 print('### ip -6 route show type prohibit')
3200 output
= check_output('ip -6 route show type prohibit')
3202 self
.assertEqual(output
, '')
3204 remove_network_unit('25-address-static.network')
3206 self
.wait_online(['dummy98:routable'])
3208 # check all routes managed by Manager are reconfigured
3209 print('### ip -4 route show type blackhole')
3210 output
= check_output('ip -4 route show type blackhole')
3212 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3214 print('### ip -4 route show type unreachable')
3215 output
= check_output('ip -4 route show type unreachable')
3217 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3219 print('### ip -4 route show type prohibit')
3220 output
= check_output('ip -4 route show type prohibit')
3222 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3224 print('### ip -6 route show type blackhole')
3225 output
= check_output('ip -6 route show type blackhole')
3227 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3229 print('### ip -6 route show type unreachable')
3230 output
= check_output('ip -6 route show type unreachable')
3232 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3234 print('### ip -6 route show type prohibit')
3235 output
= check_output('ip -6 route show type prohibit')
3237 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3239 remove_link('dummy98')
3242 # check all routes managed by Manager are removed
3243 print('### ip -4 route show type blackhole')
3244 output
= check_output('ip -4 route show type blackhole')
3246 self
.assertEqual(output
, '')
3248 print('### ip -4 route show type unreachable')
3249 output
= check_output('ip -4 route show type unreachable')
3251 self
.assertEqual(output
, '')
3253 print('### ip -4 route show type prohibit')
3254 output
= check_output('ip -4 route show type prohibit')
3256 self
.assertEqual(output
, '')
3258 print('### ip -6 route show type blackhole')
3259 output
= check_output('ip -6 route show type blackhole')
3261 self
.assertEqual(output
, '')
3263 print('### ip -6 route show type unreachable')
3264 output
= check_output('ip -6 route show type unreachable')
3266 self
.assertEqual(output
, '')
3268 print('### ip -6 route show type prohibit')
3269 output
= check_output('ip -6 route show type prohibit')
3271 self
.assertEqual(output
, '')
3275 def test_route_static(self
):
3277 for manage_foreign_routes
in [True, False]:
3283 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3284 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3285 self
._test
_route
_static
(manage_foreign_routes
)
3287 @expectedFailureIfRTA_VIAIsNotSupported()
3288 def test_route_via_ipv6(self
):
3289 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3291 self
.wait_online(['dummy98:routable'])
3293 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3296 print('### ip -6 route show dev dummy98')
3297 output
= check_output('ip -6 route show dev dummy98')
3299 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3300 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3302 print('### ip -4 route show dev dummy98')
3303 output
= check_output('ip -4 route show dev dummy98')
3305 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3306 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3308 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3309 def test_route_congctl(self
):
3310 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3312 self
.wait_online(['dummy98:routable'])
3314 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3315 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3317 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3318 self
.assertIn('congctl dctcp', output
)
3320 print('### ip -4 route show dev dummy98 149.10.124.66')
3321 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3323 self
.assertIn('149.10.124.66 proto static', output
)
3324 self
.assertIn('congctl dctcp', output
)
3325 self
.assertIn('rto_min 300s', output
)
3327 @expectedFailureIfModuleIsNotAvailable('vrf')
3328 def test_route_vrf(self
):
3329 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3330 '25-vrf.netdev', '25-vrf.network')
3332 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3334 output
= check_output('ip route show vrf vrf99')
3336 self
.assertRegex(output
, 'default via 192.168.100.1')
3338 output
= check_output('ip route show')
3340 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3342 def test_gateway_reconfigure(self
):
3343 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3345 self
.wait_online(['dummy98:routable'])
3346 print('### ip -4 route show dev dummy98 default')
3347 output
= check_output('ip -4 route show dev dummy98 default')
3349 self
.assertIn('default via 149.10.124.59 proto static', output
)
3350 self
.assertNotIn('149.10.124.60', output
)
3352 remove_network_unit('25-gateway-static.network')
3353 copy_network_unit('25-gateway-next-static.network')
3355 self
.wait_online(['dummy98:routable'])
3356 print('### ip -4 route show dev dummy98 default')
3357 output
= check_output('ip -4 route show dev dummy98 default')
3359 self
.assertNotIn('149.10.124.59', output
)
3360 self
.assertIn('default via 149.10.124.60 proto static', output
)
3362 def test_ip_route_ipv6_src_route(self
):
3363 # a dummy device does not make the addresses go through tentative state, so we
3364 # reuse a bond from an earlier test, which does make the addresses go through
3365 # tentative state, and do our test on that
3366 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3368 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3370 output
= check_output('ip -6 route list dev bond199')
3372 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3374 def test_route_preferred_source_with_existing_address(self
):
3376 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3381 networkctl_reconfigure('dummy98')
3383 self
.wait_online(['dummy98:routable'])
3385 output
= check_output('ip -6 route list dev dummy98')
3387 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3389 def test_ip_link_mac_address(self
):
3390 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3392 self
.wait_online(['dummy98:degraded'])
3394 output
= check_output('ip link show dummy98')
3396 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3398 def test_ip_link_unmanaged(self
):
3399 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3402 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3404 def test_ipv6_address_label(self
):
3405 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3407 self
.wait_online(['dummy98:degraded'])
3409 output
= check_output('ip addrlabel list')
3411 self
.assertRegex(output
, '2004:da8:1::/64')
3413 def test_ipv6_proxy_ndp(self
):
3414 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3417 self
.wait_online(['dummy98:routable'])
3419 output
= check_output('ip neighbor show proxy dev dummy98')
3421 for i
in range(1, 5):
3422 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3424 def test_neighbor_section(self
):
3425 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3427 self
.wait_online(['dummy98:degraded'])
3429 print('### ip neigh list dev dummy98')
3430 output
= check_output('ip neigh list dev dummy98')
3432 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3433 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3434 self
.assertNotIn('2004:da8:1:0::2', output
)
3435 self
.assertNotIn('192.168.10.2', output
)
3436 self
.assertNotIn('00:00:5e:00:02:67', output
)
3438 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3441 copy_network_unit('25-neighbor-section.network.d/override.conf')
3443 self
.wait_online(['dummy98:degraded'])
3445 print('### ip neigh list dev dummy98 (after reloading)')
3446 output
= check_output('ip neigh list dev dummy98')
3448 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3449 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3450 self
.assertNotIn('2004:da8:1:0::2', output
)
3451 self
.assertNotIn('192.168.10.2', output
)
3452 self
.assertNotIn('00:00:5e:00:02', output
)
3454 def test_neighbor_reconfigure(self
):
3455 copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins
=False)
3457 self
.wait_online(['dummy98:degraded'])
3459 print('### ip neigh list dev dummy98')
3460 output
= check_output('ip neigh list dev dummy98')
3462 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3463 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3465 remove_network_unit('25-neighbor-section.network')
3466 copy_network_unit('25-neighbor-next.network')
3468 self
.wait_online(['dummy98:degraded'])
3469 print('### ip neigh list dev dummy98')
3470 output
= check_output('ip neigh list dev dummy98')
3472 self
.assertNotIn('00:00:5e:00:02:65', output
)
3473 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3474 self
.assertNotIn('2004:da8:1::1', output
)
3476 def test_neighbor_gre(self
):
3477 copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
3478 '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
3480 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout
='40s')
3482 output
= check_output('ip neigh list dev gretun97')
3484 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3485 self
.assertNotIn('10.0.0.23', output
)
3487 output
= check_output('ip neigh list dev ip6gretun97')
3489 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3490 self
.assertNotIn('2001:db8:0:f102::18', output
)
3492 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3495 def test_link_local_addressing(self
):
3496 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3497 '25-link-local-addressing-no.network', '12-dummy.netdev')
3499 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3501 output
= check_output('ip address show dev test1')
3503 self
.assertRegex(output
, 'inet .* scope link')
3504 self
.assertRegex(output
, 'inet6 .* scope link')
3506 output
= check_output('ip address show dev dummy98')
3508 self
.assertNotRegex(output
, 'inet6* .* scope link')
3510 # Documentation/networking/ip-sysctl.txt
3512 # addr_gen_mode - INTEGER
3513 # Defines how link-local and autoconf addresses are generated.
3515 # 0: generate address based on EUI64 (default)
3516 # 1: do no generate a link-local address, use EUI64 for addresses generated
3518 # 2: generate stable privacy addresses, using the secret from
3519 # stable_secret (RFC7217)
3520 # 3: generate stable privacy addresses, using a random secret if unset
3522 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3523 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3524 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3526 def test_link_local_addressing_ipv6ll(self
):
3527 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3529 self
.wait_online(['dummy98:degraded'])
3531 # An IPv6LL address exists by default.
3532 output
= check_output('ip address show dev dummy98')
3534 self
.assertRegex(output
, 'inet6 .* scope link')
3536 copy_network_unit('25-link-local-addressing-no.network')
3538 self
.wait_online(['dummy98:carrier'])
3540 # Check if the IPv6LL address is removed.
3541 output
= check_output('ip address show dev dummy98')
3543 self
.assertNotRegex(output
, 'inet6 .* scope link')
3545 remove_network_unit('25-link-local-addressing-no.network')
3547 self
.wait_online(['dummy98:degraded'])
3549 # Check if a new IPv6LL address is assigned.
3550 output
= check_output('ip address show dev dummy98')
3552 self
.assertRegex(output
, 'inet6 .* scope link')
3554 def test_sysctl(self
):
3555 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3556 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3558 self
.wait_online(['dummy98:degraded'])
3560 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3561 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3562 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3563 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3564 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3565 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3566 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3567 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3568 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3570 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3572 self
.wait_online(['dummy98:degraded'])
3574 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3576 def test_sysctl_disable_ipv6(self
):
3577 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3579 print('## Disable ipv6')
3580 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3581 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3584 self
.wait_online(['dummy98:routable'])
3586 output
= check_output('ip -4 address show dummy98')
3588 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3589 output
= check_output('ip -6 address show dummy98')
3591 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3592 self
.assertRegex(output
, 'inet6 .* scope link')
3593 output
= check_output('ip -4 route show dev dummy98')
3595 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3596 output
= check_output('ip -6 route show default')
3598 self
.assertRegex(output
, 'default')
3599 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3601 remove_link('dummy98')
3603 print('## Enable ipv6')
3604 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3605 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3608 self
.wait_online(['dummy98:routable'])
3610 output
= check_output('ip -4 address show dummy98')
3612 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3613 output
= check_output('ip -6 address show dummy98')
3615 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3616 self
.assertRegex(output
, 'inet6 .* scope link')
3617 output
= check_output('ip -4 route show dev dummy98')
3619 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3620 output
= check_output('ip -6 route show default')
3622 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3624 def test_bind_carrier(self
):
3625 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3628 # no bound interface.
3629 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3630 output
= check_output('ip address show test1')
3632 self
.assertNotIn('UP,LOWER_UP', output
)
3633 self
.assertIn('DOWN', output
)
3634 self
.assertNotIn('192.168.10', output
)
3636 # add one bound interface. The interface will be up.
3637 check_output('ip link add dummy98 type dummy')
3638 check_output('ip link set dummy98 up')
3639 self
.wait_online(['test1:routable'])
3640 output
= check_output('ip address show test1')
3642 self
.assertIn('UP,LOWER_UP', output
)
3643 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3645 # add another bound interface. The interface is still up.
3646 check_output('ip link add dummy99 type dummy')
3647 check_output('ip link set dummy99 up')
3648 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3649 output
= check_output('ip address show test1')
3651 self
.assertIn('UP,LOWER_UP', output
)
3652 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3654 # remove one of the bound interfaces. The interface is still up
3655 remove_link('dummy98')
3656 output
= check_output('ip address show test1')
3658 self
.assertIn('UP,LOWER_UP', output
)
3659 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3661 # bring down the remaining bound interface. The interface will be down.
3662 check_output('ip link set dummy99 down')
3663 self
.wait_operstate('test1', 'off')
3664 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3665 output
= check_output('ip address show test1')
3667 self
.assertNotIn('UP,LOWER_UP', output
)
3668 self
.assertIn('DOWN', output
)
3669 self
.assertNotIn('192.168.10', output
)
3671 # bring up the bound interface. The interface will be up.
3672 check_output('ip link set dummy99 up')
3673 self
.wait_online(['test1:routable'])
3674 output
= check_output('ip address show test1')
3676 self
.assertIn('UP,LOWER_UP', output
)
3677 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3679 # remove the remaining bound interface. The interface will be down.
3680 remove_link('dummy99')
3681 self
.wait_operstate('test1', 'off')
3682 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3683 output
= check_output('ip address show test1')
3685 self
.assertNotIn('UP,LOWER_UP', output
)
3686 self
.assertIn('DOWN', output
)
3687 self
.assertNotIn('192.168.10', output
)
3689 # re-add one bound interface. The interface will be up.
3690 check_output('ip link add dummy98 type dummy')
3691 check_output('ip link set dummy98 up')
3692 self
.wait_online(['test1:routable'])
3693 output
= check_output('ip address show test1')
3695 self
.assertIn('UP,LOWER_UP', output
)
3696 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3698 def _test_activation_policy(self
, interface
, test
):
3699 conffile
= '25-activation-policy.network'
3701 conffile
= f
'{conffile}.d/{test}.conf'
3702 if interface
== 'vlan99':
3703 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3704 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3707 always
= test
.startswith('always')
3708 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3709 expect_up
= initial_up
3710 next_up
= not expect_up
3712 if test
.endswith('down'):
3713 self
.wait_activated(interface
)
3715 for iteration
in range(4):
3716 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3717 operstate
= 'routable' if expect_up
else 'off'
3718 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3719 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3722 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3723 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3724 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3726 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3729 check_output(f
'ip link set dev {interface} up')
3731 check_output(f
'ip link set dev {interface} down')
3732 expect_up
= initial_up
if always
else next_up
3733 next_up
= not next_up
3737 def test_activation_policy(self
):
3739 for interface
in ['test1', 'vlan99']:
3740 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3746 print(f
'### test_activation_policy(interface={interface}, test={test})')
3747 with self
.subTest(interface
=interface
, test
=test
):
3748 self
._test
_activation
_policy
(interface
, test
)
3750 def _test_activation_policy_required_for_online(self
, policy
, required
):
3751 conffile
= '25-activation-policy.network'
3752 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3754 units
+= [f
'{conffile}.d/{policy}.conf']
3756 units
+= [f
'{conffile}.d/required-{required}.conf']
3757 copy_network_unit(*units
, copy_dropins
=False)
3760 if policy
.endswith('down'):
3761 self
.wait_activated('test1')
3763 if policy
.endswith('down') or policy
== 'manual':
3764 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3766 self
.wait_online(['test1'])
3768 if policy
== 'always-down':
3769 # if always-down, required for online is forced to no
3772 # otherwise if required for online is specified, it should match that
3773 expected
= required
== 'yes'
3775 # otherwise if only policy specified, required for online defaults to
3776 # true if policy is up, always-up, or bound
3777 expected
= policy
.endswith('up') or policy
== 'bound'
3779 # default is true, if neither are specified
3782 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
3785 yesno
= 'yes' if expected
else 'no'
3786 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3788 def test_activation_policy_required_for_online(self
):
3790 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3791 for required
in ['yes', 'no', '']:
3797 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3798 with self
.subTest(policy
=policy
, required
=required
):
3799 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3801 def test_domain(self
):
3802 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3804 self
.wait_online(['dummy98:routable'])
3806 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
3808 self
.assertRegex(output
, 'Address: 192.168.42.100')
3809 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3810 self
.assertRegex(output
, 'Search Domains: one')
3812 def test_keep_configuration_static(self
):
3813 check_output('ip link add name dummy98 type dummy')
3814 check_output('ip address add 10.1.2.3/16 dev dummy98')
3815 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3816 output
= check_output('ip address show dummy98')
3818 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3819 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3820 output
= check_output('ip route show dev dummy98')
3823 copy_network_unit('24-keep-configuration-static.network')
3825 self
.wait_online(['dummy98:routable'])
3827 output
= check_output('ip address show dummy98')
3829 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3830 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3832 @expectedFailureIfNexthopIsNotAvailable()
3833 def test_nexthop(self
):
3834 def check_nexthop(self
):
3835 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3837 output
= check_output('ip nexthop list dev veth99')
3839 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3840 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3841 self
.assertIn('id 3 dev veth99', output
)
3842 self
.assertIn('id 4 dev veth99', output
)
3843 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3844 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3845 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3847 output
= check_output('ip nexthop list dev dummy98')
3849 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
3851 # kernel manages blackhole nexthops on lo
3852 output
= check_output('ip nexthop list dev lo')
3854 self
.assertIn('id 6 blackhole', output
)
3855 self
.assertIn('id 7 blackhole', output
)
3857 # group nexthops are shown with -0 option
3858 output
= check_output('ip -0 nexthop list id 21')
3860 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
3862 output
= check_output('ip route show dev veth99 10.10.10.10')
3864 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
3866 output
= check_output('ip route show dev veth99 10.10.10.11')
3868 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
3870 output
= check_output('ip route show dev veth99 10.10.10.12')
3872 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
3874 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3876 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3878 output
= check_output('ip route show 10.10.10.13')
3880 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
3882 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
3884 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
3886 output
= check_output('ip route show 10.10.10.14')
3888 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
3889 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
3890 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
3892 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
3895 copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
3896 '12-dummy.netdev', '25-nexthop-dummy.network')
3901 remove_network_unit('25-nexthop.network')
3902 copy_network_unit('25-nexthop-nothing.network')
3904 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3906 output
= check_output('ip nexthop list dev veth99')
3908 self
.assertEqual(output
, '')
3909 output
= check_output('ip nexthop list dev lo')
3911 self
.assertEqual(output
, '')
3913 remove_network_unit('25-nexthop-nothing.network')
3914 copy_network_unit('25-nexthop.network')
3915 networkctl_reconfigure('dummy98')
3920 remove_link('veth99')
3923 output
= check_output('ip nexthop list dev lo')
3925 self
.assertEqual(output
, '')
3927 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
3935 @expectedFailureIfModuleIsNotAvailable('sch_cake')
3936 def test_qdisc_cake(self
):
3937 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
3939 self
.wait_online(['dummy98:routable'])
3941 output
= check_output('tc qdisc show dev dummy98')
3943 self
.assertIn('qdisc cake 3a: root', output
)
3944 self
.assertIn('bandwidth 500Mbit', output
)
3945 self
.assertIn('autorate-ingress', output
)
3946 self
.assertIn('diffserv8', output
)
3947 self
.assertIn('dual-dsthost', output
)
3948 self
.assertIn(' nat', output
)
3949 self
.assertIn(' wash', output
)
3950 self
.assertIn(' split-gso', output
)
3951 self
.assertIn(' raw', output
)
3952 self
.assertIn(' atm', output
)
3953 self
.assertIn('overhead 128', output
)
3954 self
.assertIn('mpu 20', output
)
3955 self
.assertIn('fwmark 0xff00', output
)
3956 self
.assertIn('rtt 1s', output
)
3957 self
.assertIn('ack-filter-aggressive', output
)
3959 @expectedFailureIfModuleIsNotAvailable('sch_codel')
3960 def test_qdisc_codel(self
):
3961 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
3963 self
.wait_online(['dummy98:routable'])
3965 output
= check_output('tc qdisc show dev dummy98')
3967 self
.assertRegex(output
, 'qdisc codel 33: root')
3968 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
3970 @expectedFailureIfModuleIsNotAvailable('sch_drr')
3971 def test_qdisc_drr(self
):
3972 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
3974 self
.wait_online(['dummy98:routable'])
3976 output
= check_output('tc qdisc show dev dummy98')
3978 self
.assertRegex(output
, 'qdisc drr 2: root')
3979 output
= check_output('tc class show dev dummy98')
3981 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
3983 @expectedFailureIfModuleIsNotAvailable('sch_ets')
3984 def test_qdisc_ets(self
):
3985 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
3987 self
.wait_online(['dummy98:routable'])
3989 output
= check_output('tc qdisc show dev dummy98')
3992 self
.assertRegex(output
, 'qdisc ets 3a: root')
3993 self
.assertRegex(output
, 'bands 10 strict 3')
3994 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
3995 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
3997 @expectedFailureIfModuleIsNotAvailable('sch_fq')
3998 def test_qdisc_fq(self
):
3999 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4001 self
.wait_online(['dummy98:routable'])
4003 output
= check_output('tc qdisc show dev dummy98')
4005 self
.assertRegex(output
, 'qdisc fq 32: root')
4006 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4007 self
.assertRegex(output
, 'quantum 1500')
4008 self
.assertRegex(output
, 'initial_quantum 13000')
4009 self
.assertRegex(output
, 'maxrate 1Mbit')
4011 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4012 def test_qdisc_fq_codel(self
):
4013 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4015 self
.wait_online(['dummy98:routable'])
4017 output
= check_output('tc qdisc show dev dummy98')
4019 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4020 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')
4022 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4023 def test_qdisc_fq_pie(self
):
4024 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4026 self
.wait_online(['dummy98:routable'])
4028 output
= check_output('tc qdisc show dev dummy98')
4031 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4032 self
.assertRegex(output
, 'limit 200000p')
4034 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4035 def test_qdisc_gred(self
):
4036 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4038 self
.wait_online(['dummy98:routable'])
4040 output
= check_output('tc qdisc show dev dummy98')
4042 self
.assertRegex(output
, 'qdisc gred 38: root')
4043 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4045 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4046 def test_qdisc_hhf(self
):
4047 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4049 self
.wait_online(['dummy98:routable'])
4051 output
= check_output('tc qdisc show dev dummy98')
4053 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4054 self
.assertRegex(output
, 'limit 1022p')
4056 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4057 def test_qdisc_htb_fifo(self
):
4058 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4060 self
.wait_online(['dummy98:routable'])
4062 output
= check_output('tc qdisc show dev dummy98')
4064 self
.assertRegex(output
, 'qdisc htb 2: root')
4065 self
.assertRegex(output
, r
'default (0x30|30)')
4067 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4068 self
.assertRegex(output
, 'limit 100000p')
4070 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4071 self
.assertRegex(output
, 'limit 1000000')
4073 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4074 self
.assertRegex(output
, 'limit 1023p')
4076 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4078 output
= check_output('tc -d class show dev dummy98')
4080 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4081 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4082 # which is fixed in v6.3.0 by
4083 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4084 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4085 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4086 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4087 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4088 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4089 self
.assertRegex(output
, 'burst 123456')
4090 self
.assertRegex(output
, 'cburst 123457')
4092 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4093 def test_qdisc_ingress(self
):
4094 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4095 '25-qdisc-ingress.network', '11-dummy.netdev')
4097 self
.wait_online(['dummy98:routable', 'test1:routable'])
4099 output
= check_output('tc qdisc show dev dummy98')
4101 self
.assertRegex(output
, 'qdisc clsact')
4103 output
= check_output('tc qdisc show dev test1')
4105 self
.assertRegex(output
, 'qdisc ingress')
4107 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4108 def test_qdisc_netem(self
):
4109 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4110 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4112 self
.wait_online(['dummy98:routable', 'test1:routable'])
4114 output
= check_output('tc qdisc show dev dummy98')
4116 self
.assertRegex(output
, 'qdisc netem 30: root')
4117 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4119 output
= check_output('tc qdisc show dev test1')
4121 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4122 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4124 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4125 def test_qdisc_pie(self
):
4126 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4128 self
.wait_online(['dummy98:routable'])
4130 output
= check_output('tc qdisc show dev dummy98')
4132 self
.assertRegex(output
, 'qdisc pie 3a: root')
4133 self
.assertRegex(output
, 'limit 200000')
4135 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4136 def test_qdisc_qfq(self
):
4137 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4139 self
.wait_online(['dummy98:routable'])
4141 output
= check_output('tc qdisc show dev dummy98')
4143 self
.assertRegex(output
, 'qdisc qfq 2: root')
4144 output
= check_output('tc class show dev dummy98')
4146 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4147 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4149 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4150 def test_qdisc_sfb(self
):
4151 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4153 self
.wait_online(['dummy98:routable'])
4155 output
= check_output('tc qdisc show dev dummy98')
4157 self
.assertRegex(output
, 'qdisc sfb 39: root')
4158 self
.assertRegex(output
, 'limit 200000')
4160 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4161 def test_qdisc_sfq(self
):
4162 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4164 self
.wait_online(['dummy98:routable'])
4166 output
= check_output('tc qdisc show dev dummy98')
4168 self
.assertRegex(output
, 'qdisc sfq 36: root')
4169 self
.assertRegex(output
, 'perturb 5sec')
4171 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4172 def test_qdisc_tbf(self
):
4173 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4175 self
.wait_online(['dummy98:routable'])
4177 output
= check_output('tc qdisc show dev dummy98')
4179 self
.assertRegex(output
, 'qdisc tbf 35: root')
4180 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4182 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4183 def test_qdisc_teql(self
):
4184 call_quiet('rmmod sch_teql')
4186 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4188 self
.wait_links('dummy98')
4189 check_output('modprobe sch_teql max_equalizers=2')
4190 self
.wait_online(['dummy98:routable'])
4192 output
= check_output('tc qdisc show dev dummy98')
4194 self
.assertRegex(output
, 'qdisc teql1 31: root')
4196 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4204 def test_state_file(self
):
4205 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4207 self
.wait_online(['dummy98:routable'])
4209 # make link state file updated
4210 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4212 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4215 output
= read_link_state_file('dummy98')
4217 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4218 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4219 self
.assertIn('ADMIN_STATE=configured', output
)
4220 self
.assertIn('OPER_STATE=routable', output
)
4221 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4222 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4223 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4224 self
.assertIn('ACTIVATION_POLICY=up', output
)
4225 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4226 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4227 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4228 self
.assertIn('DOMAINS=hogehoge', output
)
4229 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4230 self
.assertIn('LLMNR=no', output
)
4231 self
.assertIn('MDNS=yes', output
)
4232 self
.assertIn('DNSSEC=no', output
)
4234 check_output(*resolvectl_cmd
, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env
=env
)
4235 check_output(*resolvectl_cmd
, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env
=env
)
4236 check_output(*resolvectl_cmd
, 'llmnr', 'dummy98', 'yes', env
=env
)
4237 check_output(*resolvectl_cmd
, 'mdns', 'dummy98', 'no', env
=env
)
4238 check_output(*resolvectl_cmd
, 'dnssec', 'dummy98', 'yes', env
=env
)
4239 check_output(*timedatectl_cmd
, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env
=env
)
4241 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4244 output
= read_link_state_file('dummy98')
4246 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4247 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4248 self
.assertIn('DOMAINS=hogehogehoge', output
)
4249 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4250 self
.assertIn('LLMNR=yes', output
)
4251 self
.assertIn('MDNS=no', output
)
4252 self
.assertIn('DNSSEC=yes', output
)
4254 check_output(*timedatectl_cmd
, 'revert', 'dummy98', env
=env
)
4256 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4259 output
= read_link_state_file('dummy98')
4261 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4262 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4263 self
.assertIn('DOMAINS=hogehogehoge', output
)
4264 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4265 self
.assertIn('LLMNR=yes', output
)
4266 self
.assertIn('MDNS=no', output
)
4267 self
.assertIn('DNSSEC=yes', output
)
4269 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
4271 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
4274 output
= read_link_state_file('dummy98')
4276 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4277 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4278 self
.assertIn('DOMAINS=hogehoge', output
)
4279 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4280 self
.assertIn('LLMNR=no', output
)
4281 self
.assertIn('MDNS=yes', output
)
4282 self
.assertIn('DNSSEC=no', output
)
4284 def test_address_state(self
):
4285 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4288 self
.wait_online(['dummy98:degraded'])
4290 output
= read_link_state_file('dummy98')
4291 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4292 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4294 # with a routable IPv4 address
4295 check_output('ip address add 10.1.2.3/16 dev dummy98')
4296 self
.wait_online(['dummy98:routable'], ipv4
=True)
4297 self
.wait_online(['dummy98:routable'])
4299 output
= read_link_state_file('dummy98')
4300 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4301 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4303 check_output('ip address del 10.1.2.3/16 dev dummy98')
4305 # with a routable IPv6 address
4306 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4307 self
.wait_online(['dummy98:routable'], ipv6
=True)
4308 self
.wait_online(['dummy98:routable'])
4310 output
= read_link_state_file('dummy98')
4311 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4312 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4314 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4322 def test_bond_keep_master(self
):
4323 check_output('ip link add bond199 type bond mode active-backup')
4324 check_output('ip link add dummy98 type dummy')
4325 check_output('ip link set dummy98 master bond199')
4327 copy_network_unit('23-keep-master.network')
4329 self
.wait_online(['dummy98:enslaved'])
4331 output
= check_output('ip -d link show bond199')
4333 self
.assertRegex(output
, 'active_slave dummy98')
4335 output
= check_output('ip -d link show dummy98')
4337 self
.assertRegex(output
, 'master bond199')
4339 def test_bond_active_slave(self
):
4340 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4342 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4344 output
= check_output('ip -d link show bond199')
4346 self
.assertIn('active_slave dummy98', output
)
4348 def test_bond_primary_slave(self
):
4349 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4351 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4353 output
= check_output('ip -d link show bond199')
4355 self
.assertIn('primary dummy98', output
)
4358 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4359 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4360 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4361 f
.write(f
'[Link]\nMACAddress={mac}\n')
4364 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4366 output
= check_output('ip -d link show bond199')
4368 self
.assertIn(f
'link/ether {mac}', output
)
4370 def test_bond_operstate(self
):
4371 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4372 '25-bond99.network', '25-bond-slave.network')
4374 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4376 output
= check_output('ip -d link show dummy98')
4378 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4380 output
= check_output('ip -d link show test1')
4382 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4384 output
= check_output('ip -d link show bond99')
4386 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4388 self
.wait_operstate('dummy98', 'enslaved')
4389 self
.wait_operstate('test1', 'enslaved')
4390 self
.wait_operstate('bond99', 'routable')
4392 check_output('ip link set dummy98 down')
4394 self
.wait_operstate('dummy98', 'off')
4395 self
.wait_operstate('test1', 'enslaved')
4396 self
.wait_operstate('bond99', 'routable')
4398 check_output('ip link set dummy98 up')
4400 self
.wait_operstate('dummy98', 'enslaved')
4401 self
.wait_operstate('test1', 'enslaved')
4402 self
.wait_operstate('bond99', 'routable')
4404 check_output('ip link set dummy98 down')
4405 check_output('ip link set test1 down')
4407 self
.wait_operstate('dummy98', 'off')
4408 self
.wait_operstate('test1', 'off')
4410 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4411 # Huh? Kernel does not recognize that all slave interfaces are down?
4412 # Let's confirm that networkd's operstate is consistent with ip's result.
4413 self
.assertNotRegex(output
, 'NO-CARRIER')
4415 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4423 def test_bridge_vlan(self
):
4424 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4425 '26-bridge.netdev', '26-bridge-vlan-master.network',
4428 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4430 output
= check_output('bridge vlan show dev test1')
4432 # check if the default VID is removed
4433 self
.assertNotIn('1 Egress Untagged', output
)
4434 for i
in range(1000, 3000):
4436 self
.assertIn(f
'{i} PVID', output
)
4437 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4438 self
.assertIn(f
'{i} Egress Untagged', output
)
4439 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4440 self
.assertIn(f
'{i}', output
)
4442 self
.assertNotIn(f
'{i}', output
)
4444 output
= check_output('bridge vlan show dev bridge99')
4446 # check if the default VID is removed
4447 self
.assertNotIn('1 Egress Untagged', output
)
4448 for i
in range(1000, 3000):
4450 self
.assertIn(f
'{i} PVID', output
)
4451 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4452 self
.assertIn(f
'{i} Egress Untagged', output
)
4453 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4454 self
.assertIn(f
'{i}', output
)
4456 self
.assertNotIn(f
'{i}', output
)
4459 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4460 '26-bridge-vlan-master.network.d/10-override.conf')
4462 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4464 output
= check_output('bridge vlan show dev test1')
4466 for i
in range(1000, 3000):
4468 self
.assertIn(f
'{i} PVID', output
)
4469 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4470 self
.assertIn(f
'{i} Egress Untagged', output
)
4471 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4472 self
.assertIn(f
'{i}', output
)
4474 self
.assertNotIn(f
'{i}', output
)
4476 output
= check_output('bridge vlan show dev bridge99')
4478 for i
in range(1000, 3000):
4480 self
.assertIn(f
'{i} PVID', output
)
4481 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4482 self
.assertIn(f
'{i} Egress Untagged', output
)
4483 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4484 self
.assertIn(f
'{i}', output
)
4486 self
.assertNotIn(f
'{i}', output
)
4488 # Remove several vlan IDs
4489 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4490 '26-bridge-vlan-master.network.d/20-override.conf')
4492 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4494 output
= check_output('bridge vlan show dev test1')
4496 for i
in range(1000, 3000):
4498 self
.assertIn(f
'{i} PVID', output
)
4499 elif i
in range(2012, 2016):
4500 self
.assertIn(f
'{i} Egress Untagged', output
)
4501 elif i
in range(2008, 2014):
4502 self
.assertIn(f
'{i}', output
)
4504 self
.assertNotIn(f
'{i}', output
)
4506 output
= check_output('bridge vlan show dev bridge99')
4508 for i
in range(1000, 3000):
4510 self
.assertIn(f
'{i} PVID', output
)
4511 elif i
in range(2022, 2026):
4512 self
.assertIn(f
'{i} Egress Untagged', output
)
4513 elif i
in range(2018, 2024):
4514 self
.assertIn(f
'{i}', output
)
4516 self
.assertNotIn(f
'{i}', output
)
4518 # Remove all vlan IDs
4519 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4520 '26-bridge-vlan-master.network.d/30-override.conf')
4522 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4524 output
= check_output('bridge vlan show dev test1')
4526 self
.assertNotIn('PVID', output
)
4527 for i
in range(1000, 3000):
4528 self
.assertNotIn(f
'{i}', output
)
4530 output
= check_output('bridge vlan show dev bridge99')
4532 self
.assertNotIn('PVID', output
)
4533 for i
in range(1000, 3000):
4534 self
.assertNotIn(f
'{i}', output
)
4536 def test_bridge_vlan_issue_20373(self
):
4537 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4538 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4539 '21-vlan.netdev', '21-vlan.network')
4541 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4543 output
= check_output('bridge vlan show dev test1')
4545 self
.assertIn('100 PVID Egress Untagged', output
)
4546 self
.assertIn('560', output
)
4547 self
.assertIn('600', output
)
4549 output
= check_output('bridge vlan show dev bridge99')
4551 self
.assertIn('1 PVID Egress Untagged', output
)
4552 self
.assertIn('100', output
)
4553 self
.assertIn('600', output
)
4555 def test_bridge_mdb(self
):
4556 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4557 '26-bridge.netdev', '26-bridge-mdb-master.network')
4559 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4561 output
= check_output('bridge mdb show dev bridge99')
4563 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4564 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4566 # Old kernel may not support bridge MDB entries on bridge master
4567 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4568 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4569 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4571 def test_bridge_keep_master(self
):
4572 check_output('ip link add bridge99 type bridge')
4573 check_output('ip link set bridge99 up')
4574 check_output('ip link add dummy98 type dummy')
4575 check_output('ip link set dummy98 master bridge99')
4577 copy_network_unit('23-keep-master.network')
4579 self
.wait_online(['dummy98:enslaved'])
4581 output
= check_output('ip -d link show dummy98')
4583 self
.assertRegex(output
, 'master bridge99')
4584 self
.assertRegex(output
, 'bridge')
4586 output
= check_output('bridge -d link show dummy98')
4588 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4589 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4590 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4591 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4592 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4593 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4594 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4595 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4596 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4597 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4598 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4599 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4601 def test_bridge_property(self
):
4602 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4603 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4604 '25-bridge99.network')
4606 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4608 output
= check_output('ip -d link show bridge99')
4610 self
.assertIn('mtu 9000 ', output
)
4612 output
= check_output('ip -d link show test1')
4614 self
.assertIn('master bridge99 ', output
)
4615 self
.assertIn('bridge_slave', output
)
4616 self
.assertIn('mtu 9000 ', output
)
4618 output
= check_output('ip -d link show dummy98')
4620 self
.assertIn('master bridge99 ', output
)
4621 self
.assertIn('bridge_slave', output
)
4622 self
.assertIn('mtu 9000 ', output
)
4624 output
= check_output('ip addr show bridge99')
4626 self
.assertIn('192.168.0.15/24', output
)
4628 output
= check_output('bridge -d link show dummy98')
4630 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4631 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4632 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4633 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4634 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4635 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4636 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4637 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4638 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4639 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4640 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4641 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4642 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4644 output
= check_output('bridge -d link show test1')
4646 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4648 check_output('ip address add 192.168.0.16/24 dev bridge99')
4649 output
= check_output('ip addr show bridge99')
4651 self
.assertIn('192.168.0.16/24', output
)
4654 print('### ip -6 route list table all dev bridge99')
4655 output
= check_output('ip -6 route list table all dev bridge99')
4657 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4659 remove_link('test1')
4660 self
.wait_operstate('bridge99', 'routable')
4662 output
= check_output('ip -d link show bridge99')
4664 self
.assertIn('mtu 9000 ', output
)
4666 output
= check_output('ip -d link show dummy98')
4668 self
.assertIn('master bridge99 ', output
)
4669 self
.assertIn('bridge_slave', output
)
4670 self
.assertIn('mtu 9000 ', output
)
4672 remove_link('dummy98')
4673 self
.wait_operstate('bridge99', 'no-carrier')
4675 output
= check_output('ip -d link show bridge99')
4677 # When no carrier, the kernel may reset the MTU
4678 self
.assertIn('NO-CARRIER', output
)
4680 output
= check_output('ip address show bridge99')
4682 self
.assertNotIn('192.168.0.15/24', output
)
4683 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4685 print('### ip -6 route list table all dev bridge99')
4686 output
= check_output('ip -6 route list table all dev bridge99')
4688 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4690 check_output('ip link add dummy98 type dummy')
4691 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4693 output
= check_output('ip -d link show bridge99')
4695 self
.assertIn('mtu 9000 ', output
)
4697 output
= check_output('ip -d link show dummy98')
4699 self
.assertIn('master bridge99 ', output
)
4700 self
.assertIn('bridge_slave', output
)
4701 self
.assertIn('mtu 9000 ', output
)
4703 def test_bridge_configure_without_carrier(self
):
4704 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4708 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4709 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4710 with self
.subTest(test
=test
):
4711 if test
== 'no-slave':
4712 # bridge has no slaves; it's up but *might* not have carrier
4713 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4714 # due to a bug in the kernel, newly-created bridges are brought up
4715 # *with* carrier, unless they have had any setting changed; e.g.
4716 # their mac set, priority set, etc. Then, they will lose carrier
4717 # as soon as a (down) slave interface is added, and regain carrier
4718 # again once the slave interface is brought up.
4719 #self.check_link_attr('bridge99', 'carrier', '0')
4720 elif test
== 'add-slave':
4721 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4722 self
.check_link_attr('test1', 'operstate', 'down')
4723 check_output('ip link set dev test1 master bridge99')
4724 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4725 self
.check_link_attr('bridge99', 'carrier', '0')
4726 elif test
== 'slave-up':
4727 # bring up slave, which will have carrier; bridge gains carrier
4728 check_output('ip link set dev test1 up')
4729 self
.wait_online(['bridge99:routable'])
4730 self
.check_link_attr('bridge99', 'carrier', '1')
4731 elif test
== 'slave-no-carrier':
4732 # drop slave carrier; bridge loses carrier
4733 check_output('ip link set dev test1 carrier off')
4734 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4735 self
.check_link_attr('bridge99', 'carrier', '0')
4736 elif test
== 'slave-carrier':
4737 # restore slave carrier; bridge gains carrier
4738 check_output('ip link set dev test1 carrier on')
4739 self
.wait_online(['bridge99:routable'])
4740 self
.check_link_attr('bridge99', 'carrier', '1')
4741 elif test
== 'slave-down':
4742 # bring down slave; bridge loses carrier
4743 check_output('ip link set dev test1 down')
4744 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4745 self
.check_link_attr('bridge99', 'carrier', '0')
4747 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
4748 self
.assertRegex(output
, '10.1.2.3')
4749 self
.assertRegex(output
, '10.1.2.1')
4751 def test_bridge_ignore_carrier_loss(self
):
4752 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4753 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4754 '25-bridge99-ignore-carrier-loss.network')
4756 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4758 check_output('ip address add 192.168.0.16/24 dev bridge99')
4759 remove_link('test1', 'dummy98')
4762 output
= check_output('ip address show bridge99')
4764 self
.assertRegex(output
, 'NO-CARRIER')
4765 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4766 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
4768 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
4769 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4770 '25-bridge99-ignore-carrier-loss.network')
4772 self
.wait_online(['bridge99:no-carrier'])
4774 for trial
in range(4):
4775 check_output('ip link add dummy98 type dummy')
4776 check_output('ip link set dummy98 up')
4778 remove_link('dummy98')
4780 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4782 output
= check_output('ip address show bridge99')
4784 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4786 output
= check_output('ip rule list table 100')
4788 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
4790 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
4798 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4799 def test_sriov(self
):
4800 copy_network_unit('25-default.link', '25-sriov.network')
4802 call('modprobe netdevsim')
4804 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4807 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
4811 self
.wait_online(['eni99np1:routable'])
4813 output
= check_output('ip link show dev eni99np1')
4815 self
.assertRegex(output
,
4816 '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 *'
4817 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4818 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4821 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4822 def test_sriov_udev(self
):
4823 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
4825 call('modprobe netdevsim')
4827 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4831 self
.wait_online(['eni99np1:routable'])
4833 # the name eni99np1 may be an alternative name.
4834 ifname
= link_resolve('eni99np1')
4836 output
= check_output('ip link show dev eni99np1')
4838 self
.assertRegex(output
,
4839 '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 *'
4840 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4841 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4843 self
.assertNotIn('vf 3', output
)
4844 self
.assertNotIn('vf 4', output
)
4846 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4847 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
4850 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4852 output
= check_output('ip link show dev eni99np1')
4854 self
.assertRegex(output
,
4855 '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 *'
4856 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4857 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4860 self
.assertNotIn('vf 4', output
)
4862 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4863 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4866 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4868 output
= check_output('ip link show dev eni99np1')
4870 self
.assertRegex(output
,
4871 '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 *'
4872 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4873 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4876 self
.assertNotIn('vf 4', output
)
4878 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4879 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
4882 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4884 output
= check_output('ip link show dev eni99np1')
4886 self
.assertRegex(output
,
4887 '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 *'
4888 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
4890 self
.assertNotIn('vf 2', output
)
4891 self
.assertNotIn('vf 3', output
)
4892 self
.assertNotIn('vf 4', output
)
4894 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
4895 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
4898 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
4900 output
= check_output('ip link show dev eni99np1')
4902 self
.assertRegex(output
,
4903 '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 *'
4904 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4905 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4907 self
.assertNotIn('vf 3', output
)
4908 self
.assertNotIn('vf 4', output
)
4910 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
4918 def test_lldp(self
):
4919 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
4921 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
4923 for trial
in range(10):
4927 output
= check_output(*networkctl_cmd
, 'lldp', env
=env
)
4929 if re
.search(r
'veth99 .* veth-peer', output
):
4934 class NetworkdRATests(unittest
.TestCase
, Utilities
):
4942 def test_ipv6_prefix_delegation(self
):
4943 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
4944 self
.setup_nftset('addr6', 'ipv6_addr')
4945 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
4946 self
.setup_nftset('ifindex', 'iface_index')
4948 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4950 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4952 self
.assertRegex(output
, 'fe80::')
4953 self
.assertRegex(output
, '2002:da8:1::1')
4955 output
= check_output(*resolvectl_cmd
, 'domain', 'veth99', env
=env
)
4957 self
.assertIn('hogehoge.test', output
)
4959 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4961 self
.assertRegex(output
, '2002:da8:1:0')
4963 self
.check_netlabel('veth99', '2002:da8:1::/64')
4964 self
.check_netlabel('veth99', '2002:da8:2::/64')
4966 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
4967 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
4968 self
.check_nftset('network6', '2002:da8:1::/64')
4969 self
.check_nftset('network6', '2002:da8:2::/64')
4970 self
.check_nftset('ifindex', 'veth99')
4972 self
.teardown_nftset('addr6', 'network6', 'ifindex')
4974 def test_ipv6_token_static(self
):
4975 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
4977 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4979 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4981 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
4982 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
4983 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
4984 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
4986 def test_ipv6_token_prefixstable(self
):
4987 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
4989 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
4991 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4993 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
4994 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
4996 def test_ipv6_token_prefixstable_without_address(self
):
4997 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
4999 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5001 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5003 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5004 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5006 def test_router_preference(self
):
5007 copy_network_unit('25-veth-client.netdev',
5008 '25-veth-router-high.netdev',
5009 '25-veth-router-low.netdev',
5011 '25-veth-bridge.network',
5012 '25-veth-client.network',
5013 '25-veth-router-high.network',
5014 '25-veth-router-low.network',
5015 '25-bridge99.network')
5017 self
.wait_online(['client-p:enslaved',
5018 'router-high:degraded', 'router-high-p:enslaved',
5019 'router-low:degraded', 'router-low-p:enslaved',
5020 'bridge99:routable'])
5022 networkctl_reconfigure('client')
5023 self
.wait_online(['client:routable'])
5025 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5026 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5027 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5028 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5030 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5032 self
.assertIn('pref high', output
)
5033 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5035 self
.assertIn('pref low', output
)
5037 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5038 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5041 self
.wait_online(['client:routable'])
5043 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5044 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5045 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5046 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5048 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5050 self
.assertIn('pref high', output
)
5051 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5053 self
.assertIn('pref low', output
)
5055 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5056 def test_captive_portal(self
):
5057 copy_network_unit('25-veth-client.netdev',
5058 '25-veth-router-captive.netdev',
5060 '25-veth-client-captive.network',
5061 '25-veth-router-captive.network',
5062 '25-veth-bridge-captive.network',
5063 '25-bridge99.network')
5065 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5066 'router-captive:degraded', 'router-captivep:enslaved'])
5068 start_radvd(config_file
='captive-portal.conf')
5069 networkctl_reconfigure('client')
5070 self
.wait_online(['client:routable'])
5072 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5073 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5075 self
.assertIn('Captive Portal: http://systemd.io', output
)
5077 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5078 def test_invalid_captive_portal(self
):
5079 def radvd_write_config(captive_portal_uri
):
5080 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5081 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5083 captive_portal_uris
= [
5084 "42ěščěškd ěšč ě s",
5089 copy_network_unit('25-veth-client.netdev',
5090 '25-veth-router-captive.netdev',
5092 '25-veth-client-captive.network',
5093 '25-veth-router-captive.network',
5094 '25-veth-bridge-captive.network',
5095 '25-bridge99.network')
5097 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5098 'router-captive:degraded', 'router-captivep:enslaved'])
5100 for uri
in captive_portal_uris
:
5101 print(f
"Captive portal: {uri}")
5102 radvd_write_config(uri
)
5104 start_radvd(config_file
='bogus-captive-portal.conf')
5105 networkctl_reconfigure('client')
5106 self
.wait_online(['client:routable'])
5108 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5109 output
= check_output(*networkctl_cmd
, 'status', 'client', env
=env
)
5111 self
.assertNotIn('Captive Portal:', output
)
5113 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5121 def test_dhcp_server(self
):
5122 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5124 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5126 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5128 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5129 self
.assertIn('Gateway: 192.168.5.3', output
)
5130 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5131 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5133 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5134 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5136 def test_dhcp_server_null_server_address(self
):
5137 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5139 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5141 output
= check_output('ip --json address show dev veth-peer')
5142 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5143 print(server_address
)
5145 output
= check_output('ip --json address show dev veth99')
5146 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5147 print(client_address
)
5149 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5151 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5152 self
.assertIn(f
'Gateway: {server_address}', output
)
5153 self
.assertIn(f
'DNS: {server_address}', output
)
5154 self
.assertIn(f
'NTP: {server_address}', output
)
5156 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth-peer', env
=env
)
5157 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5159 def test_dhcp_server_with_uplink(self
):
5160 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5161 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5163 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5165 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5167 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5168 self
.assertIn('Gateway: 192.168.5.3', output
)
5169 self
.assertIn('DNS: 192.168.5.1', output
)
5170 self
.assertIn('NTP: 192.168.5.1', output
)
5172 def test_emit_router_timezone(self
):
5173 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5175 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5177 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5179 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5180 self
.assertIn('Gateway: 192.168.5.1', output
)
5181 self
.assertIn('Time Zone: Europe/Berlin', output
)
5183 def test_dhcp_server_static_lease(self
):
5184 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5186 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5188 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5190 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5191 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5193 def test_dhcp_server_static_lease_default_client_id(self
):
5194 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5196 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5198 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
5200 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5201 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5203 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5211 def test_relay_agent(self
):
5212 copy_network_unit('25-agent-veth-client.netdev',
5213 '25-agent-veth-server.netdev',
5214 '25-agent-client.network',
5215 '25-agent-server.network',
5216 '25-agent-client-peer.network',
5217 '25-agent-server-peer.network')
5220 self
.wait_online(['client:routable'])
5222 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'client', env
=env
)
5224 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5226 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5234 def test_dhcp_client_ipv6_only(self
):
5235 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5238 self
.wait_online(['veth-peer:carrier'])
5240 # information request mode
5241 # The name ipv6-only option may not be supported by older dnsmasq
5242 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5243 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5244 '--dhcp-option=option6:dns-server,[2600::ee]',
5245 '--dhcp-option=option6:ntp-server,[2600::ff]',
5246 ra_mode
='ra-stateless')
5247 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5249 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5250 # Let's wait for the expected DNS server being listed in the state file.
5251 for _
in range(100):
5252 output
= read_link_state_file('veth99')
5253 if 'DNS=2600::ee' in output
:
5257 # Check link state file
5258 print('## link state file')
5259 output
= read_link_state_file('veth99')
5261 self
.assertIn('DNS=2600::ee', output
)
5262 self
.assertIn('NTP=2600::ff', output
)
5264 # Check manager state file
5265 print('## manager state file')
5266 output
= read_manager_state_file()
5268 self
.assertRegex(output
, 'DNS=.*2600::ee')
5269 self
.assertRegex(output
, 'NTP=.*2600::ff')
5271 print('## dnsmasq log')
5272 output
= read_dnsmasq_log_file()
5274 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5275 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5276 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5277 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5278 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5281 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5286 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5287 '--dhcp-option=option6:dns-server,[2600::ee]',
5288 '--dhcp-option=option6:ntp-server,[2600::ff]')
5289 networkctl_reconfigure('veth99')
5290 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5293 output
= check_output('ip address show dev veth99 scope global')
5295 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5296 self
.assertNotIn('192.168.5', output
)
5298 # checking semi-static route
5299 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5301 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5303 # Confirm that ipv6 token is not set in the kernel
5304 output
= check_output('ip token show dev veth99')
5306 self
.assertRegex(output
, 'token :: dev veth99')
5308 # Make manager and link state file updated
5309 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5311 # Check link state file
5312 print('## link state file')
5313 output
= read_link_state_file('veth99')
5315 self
.assertIn('DNS=2600::ee', output
)
5316 self
.assertIn('NTP=2600::ff', output
)
5318 # Check manager state file
5319 print('## manager state file')
5320 output
= read_manager_state_file()
5322 self
.assertRegex(output
, 'DNS=.*2600::ee')
5323 self
.assertRegex(output
, 'NTP=.*2600::ff')
5325 print('## dnsmasq log')
5326 output
= read_dnsmasq_log_file()
5328 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5329 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5330 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5331 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5332 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5333 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5336 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5339 # Testing without rapid commit support
5340 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5341 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5344 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5345 '--dhcp-option=option6:dns-server,[2600::ee]',
5346 '--dhcp-option=option6:ntp-server,[2600::ff]')
5349 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5352 output
= check_output('ip address show dev veth99 scope global')
5354 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5355 self
.assertNotIn('192.168.5', output
)
5357 # checking semi-static route
5358 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5360 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5362 # Make manager and link state file updated
5363 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
5365 # Check link state file
5366 print('## link state file')
5367 output
= read_link_state_file('veth99')
5369 self
.assertIn('DNS=2600::ee', output
)
5370 self
.assertIn('NTP=2600::ff', output
)
5372 # Check manager state file
5373 print('## manager state file')
5374 output
= read_manager_state_file()
5376 self
.assertRegex(output
, 'DNS=.*2600::ee')
5377 self
.assertRegex(output
, 'NTP=.*2600::ff')
5379 print('## dnsmasq log')
5380 output
= read_dnsmasq_log_file()
5382 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5383 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5384 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5385 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5386 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5387 self
.assertNotIn('rapid-commit', output
)
5390 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5393 def test_dhcp_client_ipv6_dbus_status(self
):
5394 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5396 self
.wait_online(['veth-peer:carrier'])
5398 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5399 # bit set) has yet been received and the configuration does not include WithoutRA=true
5400 state
= get_dhcp6_client_state('veth99')
5401 print(f
"DHCPv6 client state = {state}")
5402 self
.assertEqual(state
, 'stopped')
5404 state
= get_dhcp4_client_state('veth99')
5405 print(f
"DHCPv4 client state = {state}")
5406 self
.assertEqual(state
, 'selecting')
5408 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5409 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5411 state
= get_dhcp6_client_state('veth99')
5412 print(f
"DHCPv6 client state = {state}")
5413 self
.assertEqual(state
, 'bound')
5415 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5416 for _
in range(100):
5417 state
= get_dhcp4_client_state('veth99')
5418 if state
== 'stopped':
5422 print(f
"DHCPv4 client state = {state}")
5423 self
.assertEqual(state
, 'stopped')
5425 # restart dnsmasq to clear log
5427 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5429 # Test renew command
5430 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5431 check_output(*networkctl_cmd
, 'renew', 'veth99', env
=env
)
5433 for _
in range(100):
5434 state
= get_dhcp4_client_state('veth99')
5435 if state
== 'stopped':
5439 print(f
"DHCPv4 client state = {state}")
5440 self
.assertEqual(state
, 'stopped')
5442 print('## dnsmasq log')
5443 output
= read_dnsmasq_log_file()
5445 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5446 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5447 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5448 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5450 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5451 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5454 self
.wait_online(['veth-peer:carrier'])
5456 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5459 output
= check_output('ip address show dev veth99 scope global')
5461 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5462 self
.assertNotIn('192.168.5', output
)
5464 print('## dnsmasq log')
5465 output
= read_dnsmasq_log_file()
5467 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5468 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5469 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5470 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5471 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5473 def test_dhcp_client_ipv4_only(self
):
5474 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5476 self
.setup_nftset('addr4', 'ipv4_addr')
5477 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5478 self
.setup_nftset('ifindex', 'iface_index')
5481 self
.wait_online(['veth-peer:carrier'])
5482 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5483 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5484 '--dhcp-option=option:domain-search,example.com',
5485 '--dhcp-alternate-port=67,5555',
5486 ipv4_range
='192.168.5.110,192.168.5.119')
5487 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5488 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5490 print('## ip address show dev veth99 scope global')
5491 output
= check_output('ip address show dev veth99 scope global')
5493 self
.assertIn('mtu 1492', output
)
5494 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5495 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')
5496 self
.assertNotIn('2600::', output
)
5498 output
= check_output('ip -4 --json address show dev veth99')
5499 for i
in json
.loads(output
)[0]['addr_info']:
5500 if i
['label'] == 'test-label':
5501 address1
= i
['local']
5504 self
.assertFalse(True)
5506 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5508 print('## ip route show table main dev veth99')
5509 output
= check_output('ip route show table main dev veth99')
5511 # no DHCP routes assigned to the main table
5512 self
.assertNotIn('proto dhcp', output
)
5514 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5515 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5516 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5517 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5519 print('## ip route show table 211 dev veth99')
5520 output
= check_output('ip route show table 211 dev veth99')
5522 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5523 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5524 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5525 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5526 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5527 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5529 print('## link state file')
5530 output
= read_link_state_file('veth99')
5532 # checking DNS server, SIP server, and Domains
5533 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5534 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5535 self
.assertIn('DOMAINS=example.com', output
)
5538 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5539 j
= json
.loads(output
)
5541 self
.assertEqual(len(j
['DNS']), 2)
5544 self
.assertEqual(i
['Family'], 2)
5545 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5546 self
.assertRegex(a
, '^192.168.5.[67]$')
5547 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5548 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5549 self
.assertEqual('192.168.5.1', a
)
5551 self
.assertEqual(len(j
['SIP']), 2)
5554 self
.assertEqual(i
['Family'], 2)
5555 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5556 self
.assertRegex(a
, '^192.168.5.2[12]$')
5557 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5558 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5559 self
.assertEqual('192.168.5.1', a
)
5561 print('## dnsmasq log')
5562 output
= read_dnsmasq_log_file()
5564 self
.assertIn('vendor class: FooBarVendorTest', output
)
5565 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5566 self
.assertIn('client provides name: test-hostname', output
)
5567 self
.assertIn('26:mtu', output
)
5569 # change address range, DNS servers, and Domains
5571 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5572 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5573 '--dhcp-option=option:domain-search,foo.example.com',
5574 '--dhcp-alternate-port=67,5555',
5575 ipv4_range
='192.168.5.120,192.168.5.129',)
5577 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5578 print('Wait for the DHCP lease to be expired')
5579 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5580 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5582 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5584 print('## ip address show dev veth99 scope global')
5585 output
= check_output('ip address show dev veth99 scope global')
5587 self
.assertIn('mtu 1492', output
)
5588 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5589 self
.assertNotIn(f
'{address1}', output
)
5590 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')
5591 self
.assertNotIn('2600::', output
)
5593 output
= check_output('ip -4 --json address show dev veth99')
5594 for i
in json
.loads(output
)[0]['addr_info']:
5595 if i
['label'] == 'test-label':
5596 address2
= i
['local']
5599 self
.assertFalse(True)
5601 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5603 print('## ip route show table main dev veth99')
5604 output
= check_output('ip route show table main dev veth99')
5606 # no DHCP routes assigned to the main table
5607 self
.assertNotIn('proto dhcp', output
)
5609 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5610 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5611 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5612 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5614 print('## ip route show table 211 dev veth99')
5615 output
= check_output('ip route show table 211 dev veth99')
5617 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5618 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5619 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5620 self
.assertNotIn('192.168.5.6', output
)
5621 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5622 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5623 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5625 print('## link state file')
5626 output
= read_link_state_file('veth99')
5628 # checking DNS server, SIP server, and Domains
5629 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5630 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5631 self
.assertIn('DOMAINS=foo.example.com', output
)
5634 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth99', env
=env
)
5635 j
= json
.loads(output
)
5637 self
.assertEqual(len(j
['DNS']), 3)
5640 self
.assertEqual(i
['Family'], 2)
5641 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5642 self
.assertRegex(a
, '^192.168.5.[178]$')
5643 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5644 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5645 self
.assertEqual('192.168.5.1', a
)
5647 self
.assertEqual(len(j
['SIP']), 2)
5650 self
.assertEqual(i
['Family'], 2)
5651 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5652 self
.assertRegex(a
, '^192.168.5.2[34]$')
5653 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5654 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5655 self
.assertEqual('192.168.5.1', a
)
5657 print('## dnsmasq log')
5658 output
= read_dnsmasq_log_file()
5660 self
.assertIn('vendor class: FooBarVendorTest', output
)
5661 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5662 self
.assertIn('client provides name: test-hostname', output
)
5663 self
.assertIn('26:mtu', output
)
5665 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5667 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5668 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5669 self
.check_nftset('ifindex', 'veth99')
5671 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5673 def test_dhcp_client_ipv4_dbus_status(self
):
5674 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5676 self
.wait_online(['veth-peer:carrier'])
5678 state
= get_dhcp4_client_state('veth99')
5679 print(f
"State = {state}")
5680 self
.assertEqual(state
, 'rebooting')
5682 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5683 '--dhcp-option=option:domain-search,example.com',
5684 '--dhcp-alternate-port=67,5555',
5685 ipv4_range
='192.168.5.110,192.168.5.119')
5686 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5687 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5689 state
= get_dhcp4_client_state('veth99')
5690 print(f
"State = {state}")
5691 self
.assertEqual(state
, 'bound')
5693 def test_dhcp_client_allow_list(self
):
5694 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5697 self
.wait_online(['veth-peer:carrier'])
5698 since
= datetime
.datetime
.now()
5701 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5703 if expect
in read_networkd_log(since
=since
):
5709 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
5710 since
= datetime
.datetime
.now()
5713 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5715 if expect
in read_networkd_log(since
=since
):
5721 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
5722 since
= datetime
.datetime
.now()
5725 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
5727 if expect
in read_networkd_log(since
=since
):
5733 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
5734 def test_dhcp_client_rapid_commit(self
):
5735 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5737 self
.wait_online(['veth-peer:carrier'])
5739 start_dnsmasq('--dhcp-rapid-commit')
5740 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5741 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5743 state
= get_dhcp4_client_state('veth99')
5744 print(f
"DHCPv4 client state = {state}")
5745 self
.assertEqual(state
, 'bound')
5747 output
= read_dnsmasq_log_file()
5748 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
5749 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
5750 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5751 self
.assertIn('DHCPACK(veth-peer)', output
)
5753 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
5754 copy_network_unit('25-veth.netdev',
5755 '25-dhcp-server-ipv6-only-mode.network',
5756 '25-dhcp-client-ipv6-only-mode.network')
5758 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
5759 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5761 state
= get_dhcp4_client_state('veth99')
5762 print(f
"State = {state}")
5763 self
.assertEqual(state
, 'bound')
5765 def test_dhcp_client_ipv4_use_routes_gateway(self
):
5767 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
5773 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
5774 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
5775 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
5777 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
5778 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
5779 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
5780 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
5781 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
5782 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
5783 copy_network_unit(*testunits
, copy_dropins
=False)
5786 self
.wait_online(['veth-peer:carrier'])
5787 additional_options
= [
5788 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
5789 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
5790 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
5793 additional_options
+= [
5794 '--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'
5796 start_dnsmasq(*additional_options
)
5797 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5799 output
= check_output('ip -4 route show dev veth99')
5805 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5806 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5807 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5808 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5809 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5811 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5812 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5813 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5814 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5816 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5817 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5818 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5819 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5820 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
5821 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5822 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5823 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5826 if use_gateway
and (not classless
or not use_routes
):
5827 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5829 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5831 # Check route to gateway
5832 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
5833 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5835 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5837 # Check RoutesToDNS= and RoutesToNTP=
5838 if dns_and_ntp_routes
:
5839 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5840 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5843 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
5844 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
5846 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
5847 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
5849 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 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
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5853 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
5854 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5855 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
5857 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
5860 def test_dhcp_client_settings_anonymize(self
):
5861 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
5863 self
.wait_online(['veth-peer:carrier'])
5865 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5867 print('## dnsmasq log')
5868 output
= read_dnsmasq_log_file()
5870 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
5871 self
.assertNotIn('test-hostname', output
)
5872 self
.assertNotIn('26:mtu', output
)
5874 def test_dhcp_keep_configuration_dhcp(self
):
5875 copy_network_unit('25-veth.netdev',
5876 '25-dhcp-server-veth-peer.network',
5877 '25-dhcp-client-keep-configuration-dhcp.network')
5879 self
.wait_online(['veth-peer:carrier'])
5881 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5883 output
= check_output('ip address show dev veth99 scope global')
5885 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5886 'valid_lft forever preferred_lft forever')
5888 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
5891 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5892 print('Wait for the DHCP lease to be expired')
5895 # The lease address should be kept after the lease expired
5896 output
= check_output('ip address show dev veth99 scope global')
5898 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5899 'valid_lft forever preferred_lft forever')
5903 # The lease address should be kept after networkd stopped
5904 output
= check_output('ip address show dev veth99 scope global')
5906 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5907 'valid_lft forever preferred_lft forever')
5909 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
5910 f
.write('[Network]\nDHCP=no\n')
5913 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5915 # Still the lease address should be kept after networkd restarted
5916 output
= check_output('ip address show dev veth99 scope global')
5918 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
5919 'valid_lft forever preferred_lft forever')
5921 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
5922 copy_network_unit('25-veth.netdev',
5923 '25-dhcp-server-veth-peer.network',
5924 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
5926 self
.wait_online(['veth-peer:carrier'])
5928 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5930 output
= check_output('ip address show dev veth99 scope global')
5932 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5937 output
= check_output('ip address show dev veth99 scope global')
5939 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5942 self
.wait_online(['veth-peer:routable'])
5944 output
= check_output('ip address show dev veth99 scope global')
5946 self
.assertNotIn('192.168.5.', output
)
5948 def test_dhcp_client_reuse_address_as_static(self
):
5949 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5951 self
.wait_online(['veth-peer:carrier'])
5953 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5955 # link become 'routable' when at least one protocol provide an valid address.
5956 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
5957 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
5959 output
= check_output('ip address show dev veth99 scope global')
5960 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
5961 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
5962 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
5963 print(static_network
)
5965 remove_network_unit('25-dhcp-client.network')
5967 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
5968 f
.write(static_network
)
5971 self
.wait_online(['veth99:routable'])
5973 output
= check_output('ip -4 address show dev veth99 scope global')
5975 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
5976 'valid_lft forever preferred_lft forever')
5978 output
= check_output('ip -6 address show dev veth99 scope global')
5980 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
5981 'valid_lft forever preferred_lft forever')
5983 @expectedFailureIfModuleIsNotAvailable('vrf')
5984 def test_dhcp_client_vrf(self
):
5985 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
5986 '25-vrf.netdev', '25-vrf.network')
5988 self
.wait_online(['veth-peer:carrier'])
5990 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
5992 # link become 'routable' when at least one protocol provide an valid address.
5993 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
5994 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
5996 print('## ip -d link show dev vrf99')
5997 output
= check_output('ip -d link show dev vrf99')
5999 self
.assertRegex(output
, 'vrf table 42')
6001 print('## ip address show vrf vrf99')
6002 output
= check_output('ip address show vrf vrf99')
6004 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6005 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6006 self
.assertRegex(output
, 'inet6 .* scope link')
6008 print('## ip address show dev veth99')
6009 output
= check_output('ip address show dev veth99')
6011 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6012 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6013 self
.assertRegex(output
, 'inet6 .* scope link')
6015 print('## ip route show vrf vrf99')
6016 output
= check_output('ip route show vrf vrf99')
6018 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6019 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6020 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6022 print('## ip route show table main dev veth99')
6023 output
= check_output('ip route show table main dev veth99')
6025 self
.assertEqual(output
, '')
6027 def test_dhcp_client_gateway_onlink_implicit(self
):
6028 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6029 '25-dhcp-client-gateway-onlink-implicit.network')
6031 self
.wait_online(['veth-peer:carrier'])
6033 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6035 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
6037 self
.assertRegex(output
, '192.168.5')
6039 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6041 self
.assertRegex(output
, 'onlink')
6042 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6044 self
.assertRegex(output
, 'onlink')
6046 def test_dhcp_client_with_ipv4ll(self
):
6047 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6048 '25-dhcp-client-with-ipv4ll.network')
6050 # we need to increase timeout above default, as this will need to wait for
6051 # systemd-networkd to get the dhcpv4 transient failure event
6052 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6054 output
= check_output('ip -4 address show dev veth99')
6056 self
.assertNotIn('192.168.5.', output
)
6057 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6060 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6061 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6062 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')
6063 self
.wait_online(['veth99:routable'])
6065 output
= check_output('ip -4 address show dev veth99')
6067 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6068 self
.assertNotIn('169.254.', output
)
6069 self
.assertNotIn('scope link', output
)
6072 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6073 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)
6074 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6076 output
= check_output('ip -4 address show dev veth99')
6078 self
.assertNotIn('192.168.5.', output
)
6079 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6081 def test_dhcp_client_use_dns(self
):
6082 def check(self
, ipv4
, ipv6
):
6083 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6084 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6085 f
.write('[DHCPv4]\nUseDNS=')
6086 f
.write('yes' if ipv4
else 'no')
6087 f
.write('\n[DHCPv6]\nUseDNS=')
6088 f
.write('yes' if ipv6
else 'no')
6089 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6092 self
.wait_online(['veth99:routable'])
6094 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6095 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6096 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6098 # make resolved re-read the link state file
6099 check_output(*resolvectl_cmd
, 'revert', 'veth99', env
=env
)
6101 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
6104 self
.assertIn('192.168.5.1', output
)
6106 self
.assertNotIn('192.168.5.1', output
)
6108 self
.assertIn('2600::1', output
)
6110 self
.assertNotIn('2600::1', output
)
6112 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6115 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6118 self
.wait_online(['veth-peer:carrier'])
6119 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6120 '--dhcp-option=option6:dns-server,[2600::1]')
6122 check(self
, True, True)
6123 check(self
, True, False)
6124 check(self
, False, True)
6125 check(self
, False, False)
6127 def test_dhcp_client_use_captive_portal(self
):
6128 def check(self
, ipv4
, ipv6
):
6129 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6130 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6131 f
.write('[DHCPv4]\nUseCaptivePortal=')
6132 f
.write('yes' if ipv4
else 'no')
6133 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6134 f
.write('yes' if ipv6
else 'no')
6135 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6138 self
.wait_online(['veth99:routable'])
6140 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6141 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6142 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6144 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6147 self
.assertIn('Captive Portal: http://systemd.io', output
)
6149 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6151 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6154 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6157 self
.wait_online(['veth-peer:carrier'])
6158 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6159 '--dhcp-option=option6:103,http://systemd.io')
6161 check(self
, True, True)
6162 check(self
, True, False)
6163 check(self
, False, True)
6164 check(self
, False, False)
6166 def test_dhcp_client_reject_captive_portal(self
):
6167 def check(self
, ipv4
, ipv6
):
6168 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6169 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6170 f
.write('[DHCPv4]\nUseCaptivePortal=')
6171 f
.write('yes' if ipv4
else 'no')
6172 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6173 f
.write('yes' if ipv6
else 'no')
6174 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6177 self
.wait_online(['veth99:routable'])
6179 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6180 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6181 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6183 output
= check_output(*networkctl_cmd
, 'status', 'veth99', env
=env
)
6185 self
.assertNotIn('Captive Portal: ', output
)
6186 self
.assertNotIn('invalid/url', output
)
6188 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6191 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6194 self
.wait_online(['veth-peer:carrier'])
6195 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6196 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6197 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6199 check(self
, True, True)
6200 check(self
, True, False)
6201 check(self
, False, True)
6202 check(self
, False, False)
6204 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6212 def test_dhcp6pd(self
):
6213 def get_dhcp6_prefix(link
):
6214 description
= get_link_description(link
)
6216 self
.assertIn('DHCPv6Client', description
.keys())
6217 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6219 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6223 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6224 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6225 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6226 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6227 '25-dhcp-pd-downstream-dummy97.network',
6228 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6229 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6232 self
.setup_nftset('addr6', 'ipv6_addr')
6233 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6234 self
.setup_nftset('ifindex', 'iface_index')
6237 self
.wait_online(['veth-peer:routable'])
6238 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6239 self
.wait_online(['veth99:degraded'])
6241 # First, test UseAddress=no and Assign=no (issue #29979).
6242 # Note, due to the bug #29701, this test must be done at first.
6243 print('### ip -6 address show dev veth99 scope global')
6244 output
= check_output('ip -6 address show dev veth99 scope global')
6246 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6248 # Check DBus assigned prefix information to veth99
6249 prefixInfo
= get_dhcp6_prefix('veth99')
6251 self
.assertEqual(len(prefixInfo
), 1)
6252 prefixInfo
= prefixInfo
[0]
6254 self
.assertIn('Prefix', prefixInfo
.keys())
6255 self
.assertIn('PrefixLength', prefixInfo
.keys())
6256 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6257 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6259 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6260 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6261 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6262 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6264 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6266 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6267 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6269 print('### ip -6 address show dev veth-peer scope global')
6270 output
= check_output('ip -6 address show dev veth-peer scope global')
6272 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6276 # dummy97: 0x01 (The link will appear later)
6278 # dummy99: auto -> 0x02 (No address assignment)
6283 print('### ip -6 address show dev veth99 scope global')
6284 output
= check_output('ip -6 address show dev veth99 scope global')
6287 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6288 # address in IA_PD (Token=static)
6289 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6290 # address in IA_PD (Token=eui64)
6291 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6292 # address in IA_PD (temporary)
6293 # Note that the temporary addresses may appear after the link enters configured state
6294 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')
6296 print('### ip -6 address show dev test1 scope global')
6297 output
= check_output('ip -6 address show dev test1 scope global')
6299 # address in IA_PD (Token=static)
6300 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6301 # address in IA_PD (temporary)
6302 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')
6304 print('### ip -6 address show dev dummy98 scope global')
6305 output
= check_output('ip -6 address show dev dummy98 scope global')
6307 # address in IA_PD (Token=static)
6308 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6309 # address in IA_PD (temporary)
6310 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')
6312 print('### ip -6 address show dev dummy99 scope global')
6313 output
= check_output('ip -6 address show dev dummy99 scope global')
6316 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6318 print('### ip -6 address show dev veth97 scope global')
6319 output
= check_output('ip -6 address show dev veth97 scope global')
6321 # address in IA_PD (Token=static)
6322 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6323 # address in IA_PD (Token=eui64)
6324 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6325 # address in IA_PD (temporary)
6326 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')
6328 print('### ip -6 address show dev veth97-peer scope global')
6329 output
= check_output('ip -6 address show dev veth97-peer scope global')
6331 # NDisc address (Token=static)
6332 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6333 # NDisc address (Token=eui64)
6334 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6335 # NDisc address (temporary)
6336 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')
6338 print('### ip -6 address show dev veth98 scope global')
6339 output
= check_output('ip -6 address show dev veth98 scope global')
6341 # address in IA_PD (Token=static)
6342 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6343 # address in IA_PD (Token=eui64)
6344 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6345 # address in IA_PD (temporary)
6346 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')
6348 print('### ip -6 address show dev veth98-peer scope global')
6349 output
= check_output('ip -6 address show dev veth98-peer scope global')
6351 # NDisc address (Token=static)
6352 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6353 # NDisc address (Token=eui64)
6354 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6355 # NDisc address (temporary)
6356 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')
6358 print('### ip -6 route show type unreachable')
6359 output
= check_output('ip -6 route show type unreachable')
6361 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6363 print('### ip -6 route show dev veth99')
6364 output
= check_output('ip -6 route show dev veth99')
6366 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6368 print('### ip -6 route show dev test1')
6369 output
= check_output('ip -6 route show dev test1')
6371 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6373 print('### ip -6 route show dev dummy98')
6374 output
= check_output('ip -6 route show dev dummy98')
6376 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6378 print('### ip -6 route show dev dummy99')
6379 output
= check_output('ip -6 route show dev dummy99')
6381 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6383 print('### ip -6 route show dev veth97')
6384 output
= check_output('ip -6 route show dev veth97')
6386 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6388 print('### ip -6 route show dev veth97-peer')
6389 output
= check_output('ip -6 route show dev veth97-peer')
6391 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6393 print('### ip -6 route show dev veth98')
6394 output
= check_output('ip -6 route show dev veth98')
6396 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6398 print('### ip -6 route show dev veth98-peer')
6399 output
= check_output('ip -6 route show dev veth98-peer')
6401 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6403 # Test case for a downstream which appears later
6404 check_output('ip link add dummy97 type dummy')
6405 self
.wait_online(['dummy97:routable'])
6407 print('### ip -6 address show dev dummy97 scope global')
6408 output
= check_output('ip -6 address show dev dummy97 scope global')
6410 # address in IA_PD (Token=static)
6411 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6412 # address in IA_PD (temporary)
6413 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')
6415 print('### ip -6 route show dev dummy97')
6416 output
= check_output('ip -6 route show dev dummy97')
6418 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6420 # Test case for reconfigure
6421 networkctl_reconfigure('dummy98', 'dummy99')
6422 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6424 print('### ip -6 address show dev dummy98 scope global')
6425 output
= check_output('ip -6 address show dev dummy98 scope global')
6427 # address in IA_PD (Token=static)
6428 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6429 # address in IA_PD (temporary)
6430 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')
6432 print('### ip -6 address show dev dummy99 scope global')
6433 output
= check_output('ip -6 address show dev dummy99 scope global')
6436 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6438 print('### ip -6 route show dev dummy98')
6439 output
= check_output('ip -6 route show dev dummy98')
6441 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6443 print('### ip -6 route show dev dummy99')
6444 output
= check_output('ip -6 route show dev dummy99')
6446 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6448 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6450 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6451 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6452 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6453 self
.check_nftset('ifindex', 'dummy98')
6455 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6457 def verify_dhcp4_6rd(self
, tunnel_name
):
6458 print('### ip -4 address show dev veth-peer scope global')
6459 output
= check_output('ip -4 address show dev veth-peer scope global')
6461 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6465 # dummy97: 0x01 (The link will appear later)
6467 # dummy99: auto -> 0x0[23] (No address assignment)
6468 # 6rd-XXX: auto -> 0x0[23]
6473 print('### ip -4 address show dev veth99 scope global')
6474 output
= check_output('ip -4 address show dev veth99 scope global')
6476 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6478 print('### ip -6 address show dev veth99 scope global')
6479 output
= check_output('ip -6 address show dev veth99 scope global')
6481 # address in IA_PD (Token=static)
6482 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6483 # address in IA_PD (Token=eui64)
6484 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6485 # address in IA_PD (temporary)
6486 # Note that the temporary addresses may appear after the link enters configured state
6487 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')
6489 print('### ip -6 address show dev test1 scope global')
6490 output
= check_output('ip -6 address show dev test1 scope global')
6492 # address in IA_PD (Token=static)
6493 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6494 # address in IA_PD (temporary)
6495 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')
6497 print('### ip -6 address show dev dummy98 scope global')
6498 output
= check_output('ip -6 address show dev dummy98 scope global')
6500 # address in IA_PD (Token=static)
6501 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6502 # address in IA_PD (temporary)
6503 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')
6505 print('### ip -6 address show dev dummy99 scope global')
6506 output
= check_output('ip -6 address show dev dummy99 scope global')
6509 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6511 print('### ip -6 address show dev veth97 scope global')
6512 output
= check_output('ip -6 address show dev veth97 scope global')
6514 # address in IA_PD (Token=static)
6515 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6516 # address in IA_PD (Token=eui64)
6517 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6518 # address in IA_PD (temporary)
6519 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')
6521 print('### ip -6 address show dev veth97-peer scope global')
6522 output
= check_output('ip -6 address show dev veth97-peer scope global')
6524 # NDisc address (Token=static)
6525 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6526 # NDisc address (Token=eui64)
6527 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6528 # NDisc address (temporary)
6529 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')
6531 print('### ip -6 address show dev veth98 scope global')
6532 output
= check_output('ip -6 address show dev veth98 scope global')
6534 # address in IA_PD (Token=static)
6535 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6536 # address in IA_PD (Token=eui64)
6537 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6538 # address in IA_PD (temporary)
6539 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')
6541 print('### ip -6 address show dev veth98-peer scope global')
6542 output
= check_output('ip -6 address show dev veth98-peer scope global')
6544 # NDisc address (Token=static)
6545 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6546 # NDisc address (Token=eui64)
6547 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6548 # NDisc address (temporary)
6549 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')
6551 print('### ip -6 route show type unreachable')
6552 output
= check_output('ip -6 route show type unreachable')
6554 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6556 print('### ip -6 route show dev veth99')
6557 output
= check_output('ip -6 route show dev veth99')
6559 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6561 print('### ip -6 route show dev test1')
6562 output
= check_output('ip -6 route show dev test1')
6564 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6566 print('### ip -6 route show dev dummy98')
6567 output
= check_output('ip -6 route show dev dummy98')
6569 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6571 print('### ip -6 route show dev dummy99')
6572 output
= check_output('ip -6 route show dev dummy99')
6574 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6576 print('### ip -6 route show dev veth97')
6577 output
= check_output('ip -6 route show dev veth97')
6579 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6581 print('### ip -6 route show dev veth97-peer')
6582 output
= check_output('ip -6 route show dev veth97-peer')
6584 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6586 print('### ip -6 route show dev veth98')
6587 output
= check_output('ip -6 route show dev veth98')
6589 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6591 print('### ip -6 route show dev veth98-peer')
6592 output
= check_output('ip -6 route show dev veth98-peer')
6594 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6596 print('### ip -6 address show dev dummy97 scope global')
6597 output
= check_output('ip -6 address show dev dummy97 scope global')
6599 # address in IA_PD (Token=static)
6600 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6601 # address in IA_PD (temporary)
6602 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')
6604 print('### ip -6 route show dev dummy97')
6605 output
= check_output('ip -6 route show dev dummy97')
6607 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6609 print(f
'### ip -d link show dev {tunnel_name}')
6610 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6612 self
.assertIn('link/sit 10.100.100.', output
)
6613 self
.assertIn('local 10.100.100.', output
)
6614 self
.assertIn('ttl 64', output
)
6615 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6616 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6618 print(f
'### ip -6 address show dev {tunnel_name}')
6619 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6621 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')
6622 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6624 print(f
'### ip -6 route show dev {tunnel_name}')
6625 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6627 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6628 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6630 print('### ip -6 route show default')
6631 output
= check_output('ip -6 route show default')
6633 self
.assertIn('default', output
)
6634 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6636 def test_dhcp4_6rd(self
):
6637 def get_dhcp_6rd_prefix(link
):
6638 description
= get_link_description(link
)
6640 self
.assertIn('DHCPv4Client', description
.keys())
6641 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6643 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6644 self
.assertIn('Prefix', prefixInfo
.keys())
6645 self
.assertIn('PrefixLength', prefixInfo
.keys())
6646 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6647 self
.assertIn('BorderRouters', prefixInfo
.keys())
6651 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6652 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6653 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6654 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6655 '25-dhcp-pd-downstream-dummy97.network',
6656 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6657 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6658 '80-6rd-tunnel.network')
6661 self
.wait_online(['veth-peer:routable'])
6664 # 6rd-prefix: 2001:db8::/32
6665 # br-addresss: 10.0.0.1
6667 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',
6668 ipv4_range
='10.100.100.100,10.100.100.200',
6669 ipv4_router
='10.0.0.1')
6670 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6671 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6673 # Check the DBus interface for assigned prefix information
6674 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6676 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6677 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6678 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6679 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6681 # Test case for a downstream which appears later
6682 check_output('ip link add dummy97 type dummy')
6683 self
.wait_online(['dummy97:routable'])
6687 for name
in os
.listdir('/sys/class/net/'):
6688 if name
.startswith('6rd-'):
6692 self
.wait_online([f
'{tunnel_name}:routable'])
6694 self
.verify_dhcp4_6rd(tunnel_name
)
6696 # Test case for reconfigure
6697 networkctl_reconfigure('dummy98', 'dummy99')
6698 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6700 self
.verify_dhcp4_6rd(tunnel_name
)
6702 print('Wait for the DHCP lease to be renewed/rebind')
6705 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6706 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6708 self
.verify_dhcp4_6rd(tunnel_name
)
6710 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
6718 def test_ipv6_route_prefix(self
):
6719 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
6720 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6723 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6725 output
= check_output('ip address show dev veth-peer')
6727 self
.assertIn('inet6 2001:db8:0:1:', output
)
6728 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6729 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6731 output
= check_output('ip -6 route show dev veth-peer')
6733 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6734 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6735 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
6736 self
.assertIn('2001:db0:fff::/64 via ', output
)
6737 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6738 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
6740 output
= check_output('ip address show dev veth99')
6742 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6743 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
6744 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
6745 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6747 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6749 self
.assertRegex(output
, '2001:db8:1:1::2')
6751 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6753 self
.assertIn('example.com', output
)
6755 output
= check_output(*networkctl_cmd
, '--json=short', 'status', env
=env
)
6758 output
= check_output(*networkctl_cmd
, '--json=short', 'status', 'veth-peer', env
=env
)
6762 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
6764 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
6765 self
.assertEqual(prefix
, '64:ff9b::')
6767 prefix_length
= pref64
['PrefixLength']
6768 self
.assertEqual(prefix_length
, 96)
6770 def test_ipv6_route_prefix_deny_list(self
):
6771 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
6772 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6775 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6777 output
= check_output('ip address show dev veth-peer')
6779 self
.assertIn('inet6 2001:db8:0:1:', output
)
6780 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6782 output
= check_output('ip -6 route show dev veth-peer')
6784 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6785 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6786 self
.assertIn('2001:db0:fff::/64 via ', output
)
6787 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6789 output
= check_output('ip address show dev veth99')
6791 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6792 self
.assertIn('inet6 2001:db8:0:2:', output
)
6794 output
= check_output(*resolvectl_cmd
, 'dns', 'veth-peer', env
=env
)
6796 self
.assertRegex(output
, '2001:db8:1:1::2')
6798 output
= check_output(*resolvectl_cmd
, 'domain', 'veth-peer', env
=env
)
6800 self
.assertIn('example.com', output
)
6802 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
6810 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
6816 self
.wait_online(['dummy98:routable'])
6817 self
.check_link_attr('dummy98', 'mtu', mtu
)
6818 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6820 # test normal restart
6822 self
.wait_online(['dummy98:routable'])
6823 self
.check_link_attr('dummy98', 'mtu', mtu
)
6824 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
6827 self
.reset_check_mtu(mtu
, ipv6_mtu
)
6829 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
6830 ''' test setting mtu/ipv6_mtu with interface already up '''
6833 # note - changing the device mtu resets the ipv6 mtu
6834 check_output('ip link set up mtu 1501 dev dummy98')
6835 check_output('ip link set up mtu 1500 dev dummy98')
6836 self
.check_link_attr('dummy98', 'mtu', '1500')
6837 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
6839 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
6841 def test_mtu_network(self
):
6842 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
6843 self
.check_mtu('1600')
6845 def test_mtu_netdev(self
):
6846 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
6847 # note - MTU set by .netdev happens ONLY at device creation!
6848 self
.check_mtu('1600', reset
=False)
6850 def test_mtu_link(self
):
6851 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
6852 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
6853 self
.check_mtu('1600', reset
=False)
6855 def test_ipv6_mtu(self
):
6856 ''' set ipv6 mtu without setting device mtu '''
6857 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
6858 self
.check_mtu('1500', '1400')
6860 def test_ipv6_mtu_toolarge(self
):
6861 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
6862 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6863 self
.check_mtu('1500', '1500')
6865 def test_mtu_network_ipv6_mtu(self
):
6866 ''' set ipv6 mtu and set device mtu via network file '''
6867 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
6868 self
.check_mtu('1600', '1550')
6870 def test_mtu_netdev_ipv6_mtu(self
):
6871 ''' set ipv6 mtu and set device mtu via netdev file '''
6872 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
6873 self
.check_mtu('1600', '1550', reset
=False)
6875 def test_mtu_link_ipv6_mtu(self
):
6876 ''' set ipv6 mtu and set device mtu via link file '''
6877 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
6878 self
.check_mtu('1600', '1550', reset
=False)
6881 if __name__
== '__main__':
6882 parser
= argparse
.ArgumentParser()
6883 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
6884 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
6885 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
6886 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
6887 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
6888 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
6889 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
6890 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
6891 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
6892 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
6893 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
6894 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
6895 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
6896 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
6897 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
6898 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
6899 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
)
6900 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
6903 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
6904 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
6905 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
6906 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
6907 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
6908 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
6909 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6910 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
6911 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
6912 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
6913 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
6914 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
6915 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
6918 networkd_bin
= ns
.networkd_bin
6920 resolved_bin
= ns
.resolved_bin
6921 if ns
.timesyncd_bin
:
6922 timesyncd_bin
= ns
.timesyncd_bin
6924 udevd_bin
= ns
.udevd_bin
6925 if ns
.wait_online_bin
:
6926 wait_online_bin
= ns
.wait_online_bin
6927 if ns
.networkctl_bin
:
6928 networkctl_bin
= ns
.networkctl_bin
6929 if ns
.resolvectl_bin
:
6930 resolvectl_bin
= ns
.resolvectl_bin
6931 if ns
.timedatectl_bin
:
6932 timedatectl_bin
= ns
.timedatectl_bin
6934 udevadm_bin
= ns
.udevadm_bin
6937 systemd_source_dir
= ns
.source_dir
6939 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
6940 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
6941 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
6943 use_valgrind
= ns
.use_valgrind
6944 enable_debug
= ns
.enable_debug
6945 asan_options
= ns
.asan_options
6946 lsan_options
= ns
.lsan_options
6947 ubsan_options
= ns
.ubsan_options
6948 with_coverage
= ns
.with_coverage
6951 # Do not forget the trailing space.
6952 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
6954 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
6955 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
6956 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
6957 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
6958 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
6961 env
.update({'ASAN_OPTIONS': asan_options
})
6963 env
.update({'LSAN_OPTIONS': lsan_options
})
6965 env
.update({'UBSAN_OPTIONS': ubsan_options
})
6967 env
.update({'SYSTEMD_MEMPOOL': '0'})
6969 wait_online_env
= env
.copy()
6971 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
6973 sys
.argv
[1:] = unknown_args
6974 unittest
.main(verbosity
=3)