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.
27 network_unit_dir
= '/run/systemd/network'
28 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
29 networkd_ci_temp_dir
= '/run/networkd-ci'
30 udev_rules_dir
= '/run/udev/rules.d'
31 credstore_dir
= '/run/credstore'
33 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
34 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
35 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
37 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
38 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
40 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
42 systemd_lib_paths
= ['/usr/lib/systemd', '/lib/systemd']
43 which_paths
= ':'.join(systemd_lib_paths
+ os
.getenv('PATH', os
.defpath
).lstrip(':').split(':'))
44 systemd_source_dir
= None
46 networkd_bin
= shutil
.which('systemd-networkd', path
=which_paths
)
47 resolved_bin
= shutil
.which('systemd-resolved', path
=which_paths
)
48 timesyncd_bin
= shutil
.which('systemd-timesyncd', path
=which_paths
)
49 udevd_bin
= shutil
.which('systemd-udevd', path
=which_paths
)
50 wait_online_bin
= shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
51 networkctl_bin
= shutil
.which('networkctl', path
=which_paths
)
52 resolvectl_bin
= shutil
.which('resolvectl', path
=which_paths
)
53 timedatectl_bin
= shutil
.which('timedatectl', path
=which_paths
)
54 udevadm_bin
= shutil
.which('udevadm', path
=which_paths
)
55 systemd_udev_rules_build_dir
= None
83 saved_ipv4_rules
= None
84 saved_ipv6_rules
= None
88 if os
.path
.exists(path
):
92 shutil
.rmtree(path
, ignore_errors
=True)
98 shutil
.copytree(src
, dst
, copy_function
=shutil
.copy
)
101 os
.makedirs(path
, exist_ok
=True)
104 pathlib
.Path(path
).touch()
106 # pylint: disable=R1710
107 def check_output(*command
, **kwargs
):
108 # This checks the result and returns stdout (and stderr) on success.
109 command
= command
[0].split() + list(command
[1:])
110 ret
= subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
, **kwargs
)
111 if ret
.returncode
== 0:
112 return ret
.stdout
.rstrip()
113 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
115 ret
.check_returncode()
117 def call(*command
, **kwargs
):
118 # This returns returncode. stdout and stderr are merged and shown in console
119 command
= command
[0].split() + list(command
[1:])
120 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).returncode
122 def call_check(*command
, **kwargs
):
123 # Same as call() above, but it triggers CalledProcessError if rc != 0
124 command
= command
[0].split() + list(command
[1:])
125 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).check_returncode()
127 def call_quiet(*command
, **kwargs
):
128 command
= command
[0].split() + list(command
[1:])
129 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
, **kwargs
).returncode
131 def run(*command
, **kwargs
):
132 # This returns CompletedProcess instance.
133 command
= command
[0].split() + list(command
[1:])
134 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, **kwargs
)
136 def check_json(string
):
139 except json
.JSONDecodeError
:
140 print(f
"String is not a valid JSON: '{string}'")
143 def is_module_available(*module_names
):
144 for module_name
in module_names
:
145 lsmod_output
= check_output('lsmod')
146 module_re
= re
.compile(rf
'^{re.escape(module_name)}\b', re
.MULTILINE
)
147 if not module_re
.search(lsmod_output
) and call_quiet('modprobe', module_name
) != 0:
151 def expectedFailureIfModuleIsNotAvailable(*module_names
):
153 return func
if is_module_available(*module_names
) else unittest
.expectedFailure(func
)
157 def expectedFailureIfERSPANv0IsNotSupported():
158 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
160 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')
161 remove_link('erspan99')
162 return func
if rc
== 0 else unittest
.expectedFailure(func
)
166 def expectedFailureIfERSPANv2IsNotSupported():
167 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
169 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')
170 remove_link('erspan99')
171 return func
if rc
== 0 else unittest
.expectedFailure(func
)
175 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
177 rc
= call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
178 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
179 return func
if rc
== 0 else unittest
.expectedFailure(func
)
183 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
185 rc
= call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
186 call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
187 return func
if rc
== 0 else unittest
.expectedFailure(func
)
191 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
194 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
195 ret
= run('ip rule list from 192.168.100.19 table 7')
196 supported
= ret
.returncode
== 0 and 'uidrange 200-300' in ret
.stdout
197 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
198 return func
if supported
else unittest
.expectedFailure(func
)
202 def expectedFailureIfNexthopIsNotAvailable():
204 rc
= call_quiet('ip nexthop list')
205 return func
if rc
== 0 else unittest
.expectedFailure(func
)
209 def expectedFailureIfRTA_VIAIsNotSupported():
211 call_quiet('ip link add dummy98 type dummy')
212 call_quiet('ip link set up dev dummy98')
213 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
214 rc
= call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
215 remove_link('dummy98')
216 return func
if rc
== 0 else unittest
.expectedFailure(func
)
220 def expectedFailureIfAlternativeNameIsNotAvailable():
222 call_quiet('ip link add dummy98 type dummy')
224 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
225 call_quiet('ip link show dev hogehogehogehogehoge') == 0
226 remove_link('dummy98')
227 return func
if supported
else unittest
.expectedFailure(func
)
231 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
233 def finalize(func
, supported
):
234 call_quiet('rmmod netdevsim')
235 return func
if supported
else unittest
.expectedFailure(func
)
237 call_quiet('rmmod netdevsim')
238 if call_quiet('modprobe netdevsim') != 0:
239 return finalize(func
, False)
242 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
245 return finalize(func
, False)
247 return finalize(func
, os
.path
.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
251 # pylint: disable=C0415
252 def compare_kernel_version(min_kernel_version
):
255 from packaging
import version
257 print('Failed to import either platform or packaging module, assuming the comparison failed')
260 # Get only the actual kernel version without any build/distro/arch stuff
261 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
262 kver
= platform
.release().split('-')[0]
263 # Get also rid of '+'
264 kver
= kver
.split('+')[0]
266 return version
.parse(kver
) >= version
.parse(min_kernel_version
)
269 check_output(*udevadm_cmd
, 'control', '--reload')
271 def copy_network_unit(*units
, copy_dropins
=True):
273 Copy networkd unit files into the testbed.
275 Any networkd unit file type can be specified, as well as drop-in files.
277 By default, all drop-ins for a specified unit file are copied in;
278 to avoid that specify dropins=False.
280 When a drop-in file is specified, its unit file is also copied in automatically.
283 mkdir_p(network_unit_dir
)
285 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
286 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
288 if unit
.endswith('.conf'):
290 unit
= os
.path
.dirname(dropin
).rstrip('.d')
291 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
293 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
295 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
297 if unit
.endswith('.link'):
303 def copy_credential(src
, target
):
304 mkdir_p(credstore_dir
)
305 cp(os
.path
.join(networkd_ci_temp_dir
, src
),
306 os
.path
.join(credstore_dir
, target
))
308 def remove_network_unit(*units
):
310 Remove previously copied unit files from the testbed.
312 Drop-ins will be removed automatically.
316 rm_f(os
.path
.join(network_unit_dir
, unit
))
317 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
319 if unit
.endswith('.link') or unit
.endswith('.link.d'):
325 def clear_network_units():
327 if os
.path
.exists(network_unit_dir
):
328 units
= os
.listdir(network_unit_dir
)
330 if unit
.endswith('.link') or unit
.endswith('.link.d'):
333 rm_rf(network_unit_dir
)
338 def copy_networkd_conf_dropin(*dropins
):
339 """Copy networkd.conf dropin files into the testbed."""
340 mkdir_p(networkd_conf_dropin_dir
)
341 for dropin
in dropins
:
342 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
344 def remove_networkd_conf_dropin(*dropins
):
345 """Remove previously copied networkd.conf dropin files from the testbed."""
346 for dropin
in dropins
:
347 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
349 def clear_networkd_conf_dropins():
350 rm_rf(networkd_conf_dropin_dir
)
352 def setup_systemd_udev_rules():
353 if not systemd_udev_rules_build_dir
:
356 mkdir_p(udev_rules_dir
)
358 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
359 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
361 for rule
in os
.listdir(path
):
362 if not rule
.endswith(".rules"):
364 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
366 def copy_udev_rule(*rules
):
367 """Copy udev rules"""
368 mkdir_p(udev_rules_dir
)
370 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
372 def remove_udev_rule(*rules
):
373 """Remove previously copied udev rules"""
375 rm_f(os
.path
.join(udev_rules_dir
, rule
))
377 def clear_udev_rules():
378 rm_rf(udev_rules_dir
)
380 def save_active_units():
381 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
382 'systemd-resolved.service', 'systemd-timesyncd.service',
383 'firewalld.service']:
384 if call(f
'systemctl is-active --quiet {u}') == 0:
385 call(f
'systemctl stop {u}')
386 active_units
.append(u
)
388 def restore_active_units():
389 if 'systemd-networkd.socket' in active_units
:
390 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
391 for u
in active_units
:
392 call(f
'systemctl restart {u}')
394 def create_unit_dropin(unit
, contents
):
395 mkdir_p(f
'/run/systemd/system/{unit}.d')
396 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
397 f
.write('\n'.join(contents
))
399 def create_service_dropin(service
, command
, additional_settings
=None):
403 f
'ExecStart=!!{valgrind_cmd}{command}',
406 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
408 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
410 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
412 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
413 if asan_options
or lsan_options
or ubsan_options
:
414 drop_in
+= ['SystemCallFilter=']
415 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
416 drop_in
+= ['MemoryDenyWriteExecute=no']
419 'Environment=SYSTEMD_MEMPOOL=0',
427 if additional_settings
:
428 drop_in
+= additional_settings
430 create_unit_dropin(f
'{service}.service', drop_in
)
432 def link_exists(link
):
433 return call_quiet(f
'ip link show {link}') == 0
435 def link_resolve(link
):
436 return check_output(f
'ip link show {link}').split(':')[1].strip()
438 def remove_link(*links
, protect
=False):
440 if protect
and link
in protected_links
:
442 if link_exists(link
):
443 call(f
'ip link del dev {link}')
445 def save_existing_links():
446 links
= os
.listdir('/sys/class/net')
448 if link_exists(link
):
449 protected_links
.add(link
)
451 print('### The following links will be protected:')
452 print(', '.join(sorted(list(protected_links
))))
455 links
= os
.listdir('/sys/class/net')
456 remove_link(*links
, protect
=True)
458 def flush_nexthops():
459 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
460 # Hence, we cannot restore nexthops in a simple way.
461 # Let's assume there is no nexthop used in the system
462 call_quiet('ip nexthop flush')
465 # pylint: disable=global-statement
467 saved_routes
= check_output('ip route show table all')
468 print('### The following routes will be protected:')
473 output
= check_output('ip route show table all')
474 for line
in output
.splitlines():
475 if line
in saved_routes
:
477 if 'proto kernel' in line
:
479 if ' dev ' in line
and not ' dev lo ' in line
:
483 print('### Removing routes that did not exist when the test started.')
485 call(f
'ip route del {line}')
487 def save_routing_policy_rules():
488 # pylint: disable=global-statement
489 global saved_ipv4_rules
, saved_ipv6_rules
491 output
= check_output(f
'ip -{ipv} rule show')
492 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
496 saved_ipv4_rules
= save(4)
497 saved_ipv6_rules
= save(6)
499 def flush_routing_policy_rules():
500 def flush(ipv
, saved_rules
):
502 output
= check_output(f
'ip -{ipv} rule show')
503 for line
in output
.splitlines():
504 if line
in saved_rules
:
508 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
510 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
511 priority
= words
[0].rstrip(':')
512 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
514 flush(4, saved_ipv4_rules
)
515 flush(6, saved_ipv6_rules
)
517 def flush_fou_ports():
518 ret
= run('ip fou show')
519 if ret
.returncode
!= 0:
520 return # fou may not be supported
521 for line
in ret
.stdout
.splitlines():
522 port
= line
.split()[1]
523 call(f
'ip fou del port {port}')
525 def flush_l2tp_tunnels():
527 ret
= run('ip l2tp show tunnel')
528 if ret
.returncode
!= 0:
529 return # l2tp may not be supported
530 for line
in ret
.stdout
.splitlines():
532 if words
[0] == 'Tunnel':
533 tid
= words
[1].rstrip(',')
534 call(f
'ip l2tp del tunnel tunnel_id {tid}')
537 # Removing L2TP tunnel is asynchronous and slightly takes a time.
540 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
541 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
545 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
548 # pylint: disable=global-statement
549 global saved_timezone
550 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
551 if r
.returncode
== 0:
552 saved_timezone
= r
.stdout
.rstrip()
553 print(f
'### Saved timezone: {saved_timezone}')
555 def restore_timezone():
557 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
559 def read_link_attr(*args
):
560 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
561 return f
.readline().strip()
563 def read_manager_state_file():
564 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
567 def read_link_state_file(link
):
568 ifindex
= read_link_attr(link
, 'ifindex')
569 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
570 with
open(path
, encoding
='utf-8') as f
:
573 def read_ip_sysctl_attr(link
, attribute
, ipv
):
574 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
575 return f
.readline().strip()
577 def read_ipv6_sysctl_attr(link
, attribute
):
578 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
580 def read_ipv4_sysctl_attr(link
, attribute
):
581 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
583 def stop_by_pid_file(pid_file
):
584 if not os
.path
.exists(pid_file
):
586 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
587 pid
= f
.read().rstrip(' \t\r\n\0')
588 os
.kill(int(pid
), signal
.SIGTERM
)
592 print(f
"PID {pid} is still alive, waiting...")
595 if e
.errno
== errno
.ESRCH
:
597 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
600 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'):
602 ra_mode
= f
',{ra_mode}'
608 f
'--log-facility={dnsmasq_log_file}',
609 '--log-queries=extra',
611 f
'--pid-file={dnsmasq_pid_file}',
612 '--conf-file=/dev/null',
614 f
'--interface={interface}',
615 f
'--dhcp-leasefile={dnsmasq_lease_file}',
617 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
618 f
'--dhcp-range={ipv4_range},2m',
619 '--dhcp-option=option:mtu,1492',
620 f
'--dhcp-option=option:router,{ipv4_router}',
623 ) + additional_options
624 check_output(*command
)
627 stop_by_pid_file(dnsmasq_pid_file
)
628 rm_f(dnsmasq_lease_file
)
629 rm_f(dnsmasq_log_file
)
631 def read_dnsmasq_log_file():
632 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
635 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
636 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
637 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
638 touch(isc_dhcpd_lease_file
)
639 check_output(isc_dhcpd_command
)
641 def stop_isc_dhcpd():
642 stop_by_pid_file(isc_dhcpd_pid_file
)
643 rm_f(isc_dhcpd_lease_file
)
645 def get_dbus_link_path(link
):
646 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
647 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
648 'GetLinkByName', 's', link
])
650 assert out
.startswith(b
'io ')
652 assert out
.endswith(b
'"')
654 return out
[:-1].split('"')[1]
656 def get_dhcp_client_state(link
, family
):
657 link_path
= get_dbus_link_path(link
)
659 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
660 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
661 assert out
.startswith(b
's "')
663 assert out
.endswith(b
'"')
664 return out
[3:-1].decode()
666 def get_dhcp4_client_state(link
):
667 return get_dhcp_client_state(link
, '4')
669 def get_dhcp6_client_state(link
):
670 return get_dhcp_client_state(link
, '6')
672 def get_link_description(link
):
673 link_path
= get_dbus_link_path(link
)
675 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
676 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
677 assert out
.startswith(b
's "')
679 assert out
.endswith(b
'"')
680 json_raw
= out
[2:].decode()
682 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
683 check_json(description
)
684 return json
.loads(description
) # Now parse the json
686 def start_radvd(*additional_options
, config_file
):
687 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
690 f
'--pidfile={radvd_pid_file}',
691 f
'--config={config_file_path}',
692 '--logmethod=stderr',
693 ) + additional_options
694 check_output(*command
)
697 stop_by_pid_file(radvd_pid_file
)
699 def radvd_check_config(config_file
):
700 if not shutil
.which('radvd'):
701 print('radvd is not installed, assuming the config check failed')
704 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
705 # set up (one instance is @unittest.skipX())
706 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
707 return call(f
'radvd --config={config_file_path} --configtest') == 0
709 def networkd_invocation_id():
710 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
712 def read_networkd_log(invocation_id
=None, since
=None):
713 if not invocation_id
:
714 invocation_id
= networkd_invocation_id()
718 '--output=short-monotonic',
719 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
722 command
.append(f
'--since={since}')
723 check_output('journalctl --sync')
724 return check_output(*command
)
726 def networkd_is_failed():
727 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
729 def stop_networkd(show_logs
=True):
731 invocation_id
= networkd_invocation_id()
732 check_output('systemctl stop systemd-networkd.socket')
733 check_output('systemctl stop systemd-networkd.service')
735 print(read_networkd_log(invocation_id
))
736 # Check if networkd exits cleanly.
737 assert not networkd_is_failed()
739 def start_networkd():
740 check_output('systemctl start systemd-networkd')
742 def restart_networkd(show_logs
=True):
744 invocation_id
= networkd_invocation_id()
745 check_output('systemctl restart systemd-networkd.service')
747 print(read_networkd_log(invocation_id
))
750 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
752 def networkctl(*args
):
753 # Do not call networkctl if networkd is in failed state.
754 # Otherwise, networkd may be restarted and we may get wrong results.
755 assert not networkd_is_failed()
756 return check_output(*(networkctl_cmd
+ list(args
)), env
=env
)
758 def networkctl_status(*args
):
759 return networkctl('-n', '0', 'status', *args
)
761 def networkctl_json(*args
):
762 return networkctl('--json=short', 'status', *args
)
764 def networkctl_reconfigure(*links
):
765 networkctl('reconfigure', *links
)
767 def networkctl_reload(sleep_time
=1):
769 # 'networkctl reload' asynchronously reconfigure links.
770 # Hence, we need to wait for a short time for link to be in configuring state.
772 time
.sleep(sleep_time
)
774 def resolvectl(*args
):
775 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
777 def timedatectl(*args
):
778 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
783 def tear_down_common():
784 # 1. stop DHCP/RA servers
790 call_quiet('rmmod netdevsim')
791 call_quiet('rmmod sch_teql')
793 # 3. remove network namespace
794 call_quiet('ip netns del ns99')
804 clear_network_units()
805 clear_networkd_conf_dropins()
810 flush_routing_policy_rules()
814 rm_rf(networkd_ci_temp_dir
)
815 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
817 clear_network_units()
818 clear_networkd_conf_dropins()
821 setup_systemd_udev_rules()
822 copy_udev_rule('00-debug-net.rules')
826 save_existing_links()
828 save_routing_policy_rules()
831 create_service_dropin('systemd-networkd', networkd_bin
,
834 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
836 'StartLimitIntervalSec=0'])
837 create_service_dropin('systemd-resolved', resolved_bin
)
838 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
840 # TODO: also run udevd with sanitizers, valgrind, or coverage
841 #create_service_dropin('systemd-udevd', udevd_bin,
842 # f'{udevadm_bin} control --reload --timeout 0')
844 'systemd-udevd.service',
848 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
852 'systemd-networkd.socket',
855 'StartLimitIntervalSec=0',
859 check_output('systemctl daemon-reload')
860 print(check_output('systemctl cat systemd-networkd.service'))
861 print(check_output('systemctl cat systemd-resolved.service'))
862 print(check_output('systemctl cat systemd-timesyncd.service'))
863 print(check_output('systemctl cat systemd-udevd.service'))
864 check_output('systemctl restart systemd-resolved.service')
865 check_output('systemctl restart systemd-timesyncd.service')
866 check_output('systemctl restart systemd-udevd.service')
868 def tearDownModule():
869 rm_rf(networkd_ci_temp_dir
)
871 clear_network_units()
872 clear_networkd_conf_dropins()
876 rm_rf('/run/systemd/system/systemd-networkd.service.d')
877 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
878 rm_rf('/run/systemd/system/systemd-resolved.service.d')
879 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
880 rm_rf('/run/systemd/system/systemd-udevd.service.d')
881 check_output('systemctl daemon-reload')
882 check_output('systemctl restart systemd-udevd.service')
883 restore_active_units()
886 # pylint: disable=no-member
888 def check_link_exists(self
, link
, expected
=True):
890 self
.assertTrue(link_exists(link
))
892 self
.assertFalse(link_exists(link
))
894 def check_link_attr(self
, *args
):
895 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
897 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
898 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
899 if allow_enoent
and not os
.path
.exists(path
):
901 with
open(path
, encoding
='utf-8') as f
:
902 self
.assertEqual(f
.readline().strip(), expected
)
904 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
905 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
907 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
908 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
910 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
911 def links_exist(*links
):
913 if not link_exists(link
):
917 for iteration
in range(timeout
+ 1):
921 if links_exist(*links
):
924 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
927 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
928 # wait for the interface is activated.
929 needle
= f
'{link}: Bringing link {state}'
931 for iteration
in range(timeout
+ 1):
934 if not link_exists(link
):
936 output
= read_networkd_log()
937 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
940 self
.fail(f
'Timed out waiting for {link} activated.')
943 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
944 """Wait for the link to reach the specified operstate and/or setup state.
946 Specify None or '' for either operstate or setup_state to ignore that state.
947 This will recheck until the state conditions are met or the timeout expires.
949 If the link successfully matches the requested state, this returns True.
950 If this times out waiting for the link to match, the behavior depends on the
951 'fail_assert' parameter; if True, this causes a test assertion failure,
952 otherwise this returns False. The default is to cause assertion failure.
954 Note that this function matches on *exactly* the given operstate and setup_state.
955 To wait for a link to reach *or exceed* a given operstate, use wait_online().
962 for secs
in range(setup_timeout
+ 1):
965 if not link_exists(link
):
967 output
= networkctl_status(link
)
968 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
972 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
975 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
976 """Wait for the links to reach the specified operstate and/or setup state.
978 This is similar to wait_operstate() but can be used for multiple links,
979 and it also calls systemd-networkd-wait-online to wait for the given operstate.
980 The operstate should be specified in the link name, like 'eth0:degraded'.
981 If just a link name is provided, wait-online's default operstate to wait for is degraded.
983 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
984 'setup_timeout' controls the per-link timeout waiting for the setup_state.
986 Set 'bool_any' to True to wait for any (instead of all) of the given links.
987 If this is set, no setup_state checks are done.
989 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
990 This is applied only for the operational state 'degraded' or above.
992 Note that this function waits for the links to reach *or exceed* the given operstate.
993 However, the setup_state, if specified, must be matched *exactly*.
995 This returns if the links reached the requested operstate/setup_state; otherwise it
996 raises CalledProcessError or fails test assertion.
998 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1006 check_output(*args
, env
=wait_online_env
)
1007 except subprocess
.CalledProcessError
:
1008 if networkd_is_failed():
1009 print('!!!!! systemd-networkd.service is failed !!!!!')
1010 call('systemctl status systemd-networkd.service')
1012 # show detailed status on failure
1013 for link
in links_with_operstate
:
1014 name
= link
.split(':')[0]
1015 if link_exists(name
):
1016 networkctl_status(name
)
1018 if not bool_any
and setup_state
:
1019 for link
in links_with_operstate
:
1020 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1022 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1023 for i
in range(timeout_sec
):
1026 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1027 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1030 self
.assertRegex(output
, address_regex
)
1032 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1033 for i
in range(timeout_sec
):
1036 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1037 if not re
.search(address_regex
, output
):
1040 self
.assertNotRegex(output
, address_regex
)
1042 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1043 for i
in range(timeout_sec
):
1046 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1047 if re
.search(route_regex
, output
):
1050 self
.assertRegex(output
, route_regex
)
1052 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1053 if not shutil
.which('selinuxenabled'):
1054 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1055 elif call_quiet('selinuxenabled') != 0:
1056 print('## Checking NetLabel skipped: SELinux disabled.')
1057 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1058 print('## Checking NetLabel skipped: netlabelctl command not found.')
1060 output
= check_output('netlabelctl unlbl list')
1062 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1064 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1065 if not shutil
.which('nft'):
1066 print('## Setting up NFT sets skipped: nft command not found.')
1068 if call(f
'nft add table inet sd_test') != 0:
1069 print('## Setting up NFT table failed.')
1071 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1072 print('## Setting up NFT sets failed.')
1075 def teardown_nftset(self
, *filters
):
1076 if not shutil
.which('nft'):
1077 print('## Tearing down NFT sets skipped: nft command not found.')
1079 for filter_name
in filters
:
1080 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1081 print('## Tearing down NFT sets failed.')
1083 if call(f
'nft delete table inet sd_test') != 0:
1084 print('## Tearing down NFT table failed.')
1087 def check_nftset(self
, filter_name
, contents
):
1088 if not shutil
.which('nft'):
1089 print('## Checking NFT sets skipped: nft command not found.')
1091 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1093 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1095 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1103 @expectedFailureIfAlternativeNameIsNotAvailable()
1104 def test_altname(self
):
1105 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1107 self
.wait_online(['dummy98:degraded'])
1109 output
= networkctl_status('dummy98')
1110 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1112 @expectedFailureIfAlternativeNameIsNotAvailable()
1113 def test_rename_to_altname(self
):
1114 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1115 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1117 self
.wait_online(['dummyalt:degraded'])
1119 output
= networkctl_status('dummyalt')
1120 self
.assertIn('hogehogehogehogehogehoge', output
)
1121 self
.assertNotIn('dummy98', output
)
1123 def test_reconfigure(self
):
1124 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1126 self
.wait_online(['dummy98:routable'])
1128 output
= check_output('ip -4 address show dev dummy98')
1130 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1131 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1132 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1134 check_output('ip address del 10.1.2.3/16 dev dummy98')
1135 check_output('ip address del 10.1.2.4/16 dev dummy98')
1136 check_output('ip address del 10.2.2.4/16 dev dummy98')
1138 networkctl_reconfigure('dummy98')
1139 self
.wait_online(['dummy98:routable'])
1141 output
= check_output('ip -4 address show dev dummy98')
1143 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1144 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1145 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1147 remove_network_unit('25-address-static.network')
1150 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1152 output
= check_output('ip -4 address show dev dummy98')
1154 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1155 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1156 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1158 copy_network_unit('25-address-static.network', copy_dropins
=False)
1160 self
.wait_online(['dummy98:routable'])
1162 output
= check_output('ip -4 address show dev dummy98')
1164 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1165 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1166 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1168 def test_renew(self
):
1170 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
1171 output
= networkctl_status('veth99')
1173 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1174 self
.assertIn('Gateway: 192.168.5.3', output
)
1175 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1176 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1178 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1181 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1183 for verb
in ['renew', 'forcerenew']:
1184 networkctl(verb
, 'veth99')
1186 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1189 def test_up_down(self
):
1190 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1192 self
.wait_online(['dummy98:routable'])
1194 networkctl('down', 'dummy98')
1195 self
.wait_online(['dummy98:off'])
1196 networkctl('up', 'dummy98')
1197 self
.wait_online(['dummy98:routable'])
1198 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1199 self
.wait_online(['dummy98:off'])
1200 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1201 self
.wait_online(['dummy98:routable'])
1203 def test_reload(self
):
1206 copy_network_unit('11-dummy.netdev')
1208 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1210 copy_network_unit('11-dummy.network')
1212 self
.wait_online(['test1:degraded'])
1214 remove_network_unit('11-dummy.network')
1216 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1218 remove_network_unit('11-dummy.netdev')
1220 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1222 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1224 self
.wait_operstate('test1', 'degraded')
1226 def test_glob(self
):
1227 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1230 self
.wait_online(['test1:degraded'])
1232 output
= networkctl('list')
1233 self
.assertRegex(output
, '1 lo ')
1234 self
.assertRegex(output
, 'test1')
1236 output
= networkctl('list', 'test1')
1237 self
.assertNotRegex(output
, '1 lo ')
1238 self
.assertRegex(output
, 'test1')
1240 output
= networkctl('list', 'te*')
1241 self
.assertNotRegex(output
, '1 lo ')
1242 self
.assertRegex(output
, 'test1')
1244 output
= networkctl_status('te*')
1245 self
.assertNotRegex(output
, '1: lo ')
1246 self
.assertRegex(output
, 'test1')
1248 output
= networkctl_status('tes[a-z][0-9]')
1249 self
.assertNotRegex(output
, '1: lo ')
1250 self
.assertRegex(output
, 'test1')
1253 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1256 self
.wait_online(['test1:degraded'])
1258 output
= networkctl_status('test1')
1259 self
.assertRegex(output
, 'MTU: 1600')
1261 def test_type(self
):
1262 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1264 self
.wait_online(['test1:degraded'])
1266 output
= networkctl_status('test1')
1268 self
.assertRegex(output
, 'Type: ether')
1270 output
= networkctl_status('lo')
1272 self
.assertRegex(output
, 'Type: loopback')
1274 def test_unit_file(self
):
1275 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1277 self
.wait_online(['test1:degraded'])
1279 output
= networkctl_status('test1')
1281 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1282 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1283 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1284 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1286 output
= read_networkd_log()
1287 self
.assertIn('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).', output
)
1289 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1290 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1291 # Let's reprocess the interface and drop the property.
1292 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1293 output
= networkctl_status('lo')
1295 self
.assertIn('Link File: n/a', output
)
1296 self
.assertIn('Network File: n/a', output
)
1298 def test_delete_links(self
):
1299 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1300 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1303 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
1305 networkctl('delete', 'test1', 'veth99')
1306 self
.check_link_exists('test1', expected
=False)
1307 self
.check_link_exists('veth99', expected
=False)
1308 self
.check_link_exists('veth-peer', expected
=False)
1310 def test_label(self
):
1313 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1321 @expectedFailureIfAlternativeNameIsNotAvailable()
1322 def test_match(self
):
1323 copy_network_unit('12-dummy-mac.netdev',
1324 '12-dummy-match-mac-01.network',
1325 '12-dummy-match-mac-02.network',
1326 '12-dummy-match-renamed.network',
1327 '12-dummy-match-altname.network',
1328 '12-dummy-altname.link')
1331 self
.wait_online(['dummy98:routable'])
1332 output
= networkctl_status('dummy98')
1333 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1334 output
= check_output('ip -4 address show dev dummy98')
1335 self
.assertIn('10.0.0.1/16', output
)
1337 check_output('ip link set dev dummy98 down')
1338 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1340 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1341 self
.wait_online(['dummy98:routable'])
1342 output
= networkctl_status('dummy98')
1343 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1345 check_output('ip link set dev dummy98 down')
1346 check_output('ip link set dev dummy98 name dummy98-1')
1348 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1349 self
.wait_online(['dummy98-1:routable'])
1350 output
= networkctl_status('dummy98-1')
1351 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1353 check_output('ip link set dev dummy98-1 down')
1354 check_output('ip link set dev dummy98-1 name dummy98-2')
1355 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1357 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1358 self
.wait_online(['dummy98-2:routable'])
1359 output
= networkctl_status('dummy98-2')
1360 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1362 def test_match_udev_property(self
):
1363 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1365 self
.wait_online(['dummy98:routable'])
1367 output
= networkctl_status('dummy98')
1369 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1371 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1379 def test_wait_online_any(self
):
1380 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1383 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
1385 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1386 self
.wait_operstate('test1', 'degraded')
1388 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1396 def test_dropin_and_name_conflict(self
):
1397 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1400 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
1402 output
= check_output('ip link show dropin-test')
1404 self
.assertRegex(output
, '00:50:56:c0:00:28')
1406 @expectedFailureIfModuleIsNotAvailable('bareudp')
1407 def test_bareudp(self
):
1408 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1411 self
.wait_online(['bareudp99:degraded'])
1413 output
= check_output('ip -d link show bareudp99')
1415 self
.assertRegex(output
, 'dstport 1000 ')
1416 self
.assertRegex(output
, 'ethertype ip ')
1418 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1419 def test_batadv(self
):
1420 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1423 self
.wait_online(['batadv99:degraded'])
1425 output
= check_output('ip -d link show batadv99')
1427 self
.assertRegex(output
, 'batadv')
1429 def test_bridge(self
):
1430 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1433 self
.wait_online(['bridge99:no-carrier'])
1435 tick
= os
.sysconf('SC_CLK_TCK')
1436 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1437 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1438 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1439 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1440 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1441 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1442 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1443 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1444 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1446 output
= networkctl_status('bridge99')
1448 self
.assertRegex(output
, 'Priority: 9')
1449 self
.assertRegex(output
, 'STP: yes')
1450 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1452 output
= check_output('ip -d link show bridge99')
1454 self
.assertIn('vlan_filtering 1 ', output
)
1455 self
.assertIn('vlan_protocol 802.1ad ', output
)
1456 self
.assertIn('vlan_default_pvid 9 ', output
)
1458 def test_bond(self
):
1459 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1462 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1464 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1465 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1466 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1467 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1468 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1469 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1470 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1471 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1472 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1473 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1474 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1476 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1477 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1479 output
= networkctl_status('bond99')
1481 self
.assertIn('Mode: 802.3ad', output
)
1482 self
.assertIn('Miimon: 1s', output
)
1483 self
.assertIn('Updelay: 2s', output
)
1484 self
.assertIn('Downdelay: 2s', output
)
1486 output
= networkctl_status('bond98')
1488 self
.assertIn('Mode: balance-tlb', output
)
1490 def test_vlan(self
):
1491 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1492 '21-vlan.network', '21-vlan-test1.network')
1495 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1497 output
= check_output('ip -d link show test1')
1499 self
.assertRegex(output
, ' mtu 2000 ')
1501 output
= check_output('ip -d link show vlan99')
1503 self
.assertIn(' mtu 2000 ', output
)
1504 self
.assertIn('REORDER_HDR', output
)
1505 self
.assertIn('LOOSE_BINDING', output
)
1506 self
.assertIn('GVRP', output
)
1507 self
.assertIn('MVRP', output
)
1508 self
.assertIn(' id 99 ', output
)
1509 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1510 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1512 output
= check_output('ip -4 address show dev test1')
1514 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1515 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1517 output
= check_output('ip -4 address show dev vlan99')
1519 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1521 def test_vlan_on_bond(self
):
1522 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1523 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1525 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1526 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1528 self
.wait_online(['bond99:off'])
1529 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1531 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1532 # that the issue is fixed by the commit, let's allow to match both string.
1533 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1537 if log_re
.search(read_networkd_log()):
1542 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1544 self
.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
1546 def test_macvtap(self
):
1548 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1554 print(f
'### test_macvtap(mode={mode})')
1555 with self
.subTest(mode
=mode
):
1556 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1557 '11-dummy.netdev', '25-macvtap.network')
1558 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1559 f
.write('[MACVTAP]\nMode=' + mode
)
1562 self
.wait_online(['macvtap99:degraded',
1563 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1565 output
= check_output('ip -d link show macvtap99')
1567 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1569 def test_macvlan(self
):
1571 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1577 print(f
'### test_macvlan(mode={mode})')
1578 with self
.subTest(mode
=mode
):
1579 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1580 '11-dummy.netdev', '25-macvlan.network')
1581 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1582 f
.write('[MACVLAN]\nMode=' + mode
)
1585 self
.wait_online(['macvlan99:degraded',
1586 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1588 output
= check_output('ip -d link show test1')
1590 self
.assertRegex(output
, ' mtu 2000 ')
1592 output
= check_output('ip -d link show macvlan99')
1594 self
.assertRegex(output
, ' mtu 2000 ')
1595 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1597 remove_link('test1')
1600 check_output("ip link add test1 type dummy")
1601 self
.wait_online(['macvlan99:degraded',
1602 'test1:carrier' if mode
== 'passthru' else 'test1:degraded'])
1604 output
= check_output('ip -d link show test1')
1606 self
.assertRegex(output
, ' mtu 2000 ')
1608 output
= check_output('ip -d link show macvlan99')
1610 self
.assertRegex(output
, ' mtu 2000 ')
1611 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1613 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1614 def test_ipvlan(self
):
1616 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1622 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1623 with self
.subTest(mode
=mode
, flag
=flag
):
1624 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1625 '11-dummy.netdev', '25-ipvlan.network')
1626 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1627 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1630 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1632 output
= check_output('ip -d link show ipvlan99')
1634 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1636 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1637 def test_ipvtap(self
):
1639 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1645 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1646 with self
.subTest(mode
=mode
, flag
=flag
):
1647 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1648 '11-dummy.netdev', '25-ipvtap.network')
1649 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1650 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1653 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1655 output
= check_output('ip -d link show ipvtap99')
1657 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1659 def test_veth(self
):
1660 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1661 '25-veth-mtu.netdev')
1664 self
.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1666 output
= check_output('ip -d link show veth99')
1668 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1669 output
= check_output('ip -d link show veth-peer')
1671 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1673 output
= check_output('ip -d link show veth-mtu')
1675 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1676 self
.assertRegex(output
, 'mtu 1800')
1677 output
= check_output('ip -d link show veth-mtu-peer')
1679 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1680 self
.assertRegex(output
, 'mtu 1800')
1682 def test_tuntap(self
):
1683 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1686 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'])
1688 pid
= networkd_pid()
1689 name
= psutil
.Process(pid
).name()[:15]
1691 output
= check_output('ip -d tuntap show')
1693 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1694 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1696 output
= check_output('ip -d link show testtun99')
1698 # Old ip command does not support IFF_ flags
1699 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1700 self
.assertIn('UP,LOWER_UP', output
)
1702 output
= check_output('ip -d link show testtap99')
1704 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1705 self
.assertIn('UP,LOWER_UP', output
)
1707 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1710 self
.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state
='unmanaged')
1712 pid
= networkd_pid()
1713 name
= psutil
.Process(pid
).name()[:15]
1715 output
= check_output('ip -d tuntap show')
1717 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1718 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1720 output
= check_output('ip -d link show testtun99')
1722 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1723 self
.assertIn('UP,LOWER_UP', output
)
1725 output
= check_output('ip -d link show testtap99')
1727 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1728 self
.assertIn('UP,LOWER_UP', output
)
1730 clear_network_units()
1732 self
.wait_online(['testtun99:off', 'testtap99:off'], setup_state
='unmanaged')
1734 output
= check_output('ip -d tuntap show')
1736 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1737 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1742 output
= check_output('ip -d link show testtun99')
1744 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1745 if 'NO-CARRIER' in output
:
1753 output
= check_output('ip -d link show testtap99')
1755 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1756 if 'NO-CARRIER' in output
:
1761 @expectedFailureIfModuleIsNotAvailable('vrf')
1763 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1766 self
.wait_online(['vrf99:carrier'])
1768 @expectedFailureIfModuleIsNotAvailable('vcan')
1769 def test_vcan(self
):
1770 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1771 '25-vcan98.netdev', '25-vcan98.network')
1774 self
.wait_online(['vcan99:carrier', 'vcan98:carrier'])
1775 # For can devices, 'carrier' is the default required operational state.
1776 self
.wait_online(['vcan99', 'vcan98'])
1778 # https://github.com/systemd/systemd/issues/30140
1779 output
= check_output('ip -d link show vcan99')
1781 self
.assertIn('mtu 16 ', output
)
1783 output
= check_output('ip -d link show vcan98')
1785 self
.assertIn('mtu 16 ', output
)
1787 @expectedFailureIfModuleIsNotAvailable('vxcan')
1788 def test_vxcan(self
):
1789 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1792 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1793 # For can devices, 'carrier' is the default required operational state.
1794 self
.wait_online(['vxcan99', 'vxcan-peer'])
1796 @expectedFailureIfModuleIsNotAvailable('wireguard')
1797 def test_wireguard(self
):
1798 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1799 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1800 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1802 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1803 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1804 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1805 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1807 self
.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1809 output
= check_output('ip -4 address show dev wg99')
1811 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1813 output
= check_output('ip -4 address show dev wg99')
1815 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1817 output
= check_output('ip -6 address show dev wg99')
1819 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1821 output
= check_output('ip -4 address show dev wg98')
1823 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1825 output
= check_output('ip -6 address show dev wg98')
1827 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1829 output
= check_output('ip -4 route show dev wg99 table 1234')
1831 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1833 output
= check_output('ip -6 route show dev wg99 table 1234')
1835 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1837 output
= check_output('ip -6 route show dev wg98 table 1234')
1839 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1840 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1841 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1842 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1843 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1844 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1845 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1846 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1847 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1848 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1849 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1850 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1851 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1852 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1853 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1854 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1855 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1856 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1857 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1858 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1859 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1860 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1861 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1862 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1863 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1864 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1865 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1866 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1867 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1868 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1869 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1870 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1871 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1872 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1873 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1886 if shutil
.which('wg'):
1889 output
= check_output('wg show wg99 listen-port')
1890 self
.assertEqual(output
, '51820')
1891 output
= check_output('wg show wg99 fwmark')
1892 self
.assertEqual(output
, '0x4d2')
1893 output
= check_output('wg show wg99 private-key')
1894 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1895 output
= check_output('wg show wg99 allowed-ips')
1896 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1897 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1898 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1899 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1900 output
= check_output('wg show wg99 persistent-keepalive')
1901 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1902 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1903 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1904 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1905 output
= check_output('wg show wg99 endpoints')
1906 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1907 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1908 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1909 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1910 output
= check_output('wg show wg99 preshared-keys')
1911 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1912 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1913 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1914 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1916 output
= check_output('wg show wg98 private-key')
1917 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1919 output
= check_output('wg show wg97 listen-port')
1920 self
.assertEqual(output
, '51821')
1921 output
= check_output('wg show wg97 fwmark')
1922 self
.assertEqual(output
, '0x4d3')
1924 def test_geneve(self
):
1925 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1928 self
.wait_online(['geneve99:degraded'])
1930 output
= check_output('ip -d link show geneve99')
1932 self
.assertRegex(output
, '192.168.22.1')
1933 self
.assertRegex(output
, '6082')
1934 self
.assertRegex(output
, 'udpcsum')
1935 self
.assertRegex(output
, 'udp6zerocsumrx')
1937 def test_ipip_tunnel(self
):
1938 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1939 '25-ipip-tunnel.netdev', '25-tunnel.network',
1940 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1941 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1942 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1944 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1946 output
= check_output('ip -d link show ipiptun99')
1948 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1949 output
= check_output('ip -d link show ipiptun98')
1951 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1952 output
= check_output('ip -d link show ipiptun97')
1954 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1955 output
= check_output('ip -d link show ipiptun96')
1957 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1959 def test_gre_tunnel(self
):
1960 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1961 '25-gre-tunnel.netdev', '25-tunnel.network',
1962 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1963 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1964 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1966 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1968 output
= check_output('ip -d link show gretun99')
1970 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1971 self
.assertRegex(output
, 'ikey 1.2.3.103')
1972 self
.assertRegex(output
, 'okey 1.2.4.103')
1973 self
.assertRegex(output
, 'iseq')
1974 self
.assertRegex(output
, 'oseq')
1975 output
= check_output('ip -d link show gretun98')
1977 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1978 self
.assertRegex(output
, 'ikey 0.0.0.104')
1979 self
.assertRegex(output
, 'okey 0.0.0.104')
1980 self
.assertNotRegex(output
, 'iseq')
1981 self
.assertNotRegex(output
, 'oseq')
1982 output
= check_output('ip -d link show gretun97')
1984 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1985 self
.assertRegex(output
, 'ikey 0.0.0.105')
1986 self
.assertRegex(output
, 'okey 0.0.0.105')
1987 self
.assertNotRegex(output
, 'iseq')
1988 self
.assertNotRegex(output
, 'oseq')
1989 output
= check_output('ip -d link show gretun96')
1991 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1992 self
.assertRegex(output
, 'ikey 0.0.0.106')
1993 self
.assertRegex(output
, 'okey 0.0.0.106')
1994 self
.assertNotRegex(output
, 'iseq')
1995 self
.assertNotRegex(output
, 'oseq')
1997 def test_ip6gre_tunnel(self
):
1998 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
1999 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2000 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2001 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2002 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2005 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2007 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2009 output
= check_output('ip -d link show ip6gretun99')
2011 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2012 output
= check_output('ip -d link show ip6gretun98')
2014 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2015 output
= check_output('ip -d link show ip6gretun97')
2017 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2018 output
= check_output('ip -d link show ip6gretun96')
2020 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2022 def test_gretap_tunnel(self
):
2023 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2024 '25-gretap-tunnel.netdev', '25-tunnel.network',
2025 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2027 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
2029 output
= check_output('ip -d link show gretap99')
2031 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2032 self
.assertRegex(output
, 'ikey 0.0.0.106')
2033 self
.assertRegex(output
, 'okey 0.0.0.106')
2034 self
.assertRegex(output
, 'iseq')
2035 self
.assertRegex(output
, 'oseq')
2036 self
.assertIn('nopmtudisc', output
)
2037 self
.assertIn('ignore-df', output
)
2038 output
= check_output('ip -d link show gretap98')
2040 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2041 self
.assertRegex(output
, 'ikey 0.0.0.107')
2042 self
.assertRegex(output
, 'okey 0.0.0.107')
2043 self
.assertRegex(output
, 'iseq')
2044 self
.assertRegex(output
, 'oseq')
2046 def test_ip6gretap_tunnel(self
):
2047 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2048 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2049 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2051 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
2053 output
= check_output('ip -d link show ip6gretap99')
2055 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2056 output
= check_output('ip -d link show ip6gretap98')
2058 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2060 def test_vti_tunnel(self
):
2061 copy_network_unit('12-dummy.netdev', '25-vti.network',
2062 '25-vti-tunnel.netdev', '25-tunnel.network',
2063 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2064 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2065 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2067 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
2069 output
= check_output('ip -d link show vtitun99')
2071 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2072 output
= check_output('ip -d link show vtitun98')
2074 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2075 output
= check_output('ip -d link show vtitun97')
2077 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2078 output
= check_output('ip -d link show vtitun96')
2080 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2082 def test_vti6_tunnel(self
):
2083 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2084 '25-vti6-tunnel.netdev', '25-tunnel.network',
2085 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2086 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2088 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
2090 output
= check_output('ip -d link show vti6tun99')
2092 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2093 output
= check_output('ip -d link show vti6tun98')
2095 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2096 output
= check_output('ip -d link show vti6tun97')
2098 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2100 def test_ip6tnl_tunnel(self
):
2101 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2102 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2103 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2104 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2105 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2106 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2107 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2109 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2110 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2111 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
2113 output
= check_output('ip -d link show ip6tnl99')
2115 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2116 output
= check_output('ip -d link show ip6tnl98')
2118 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2119 output
= check_output('ip -d link show ip6tnl97')
2121 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2122 output
= check_output('ip -d link show ip6tnl-external')
2124 self
.assertIn('ip6tnl-external@NONE:', output
)
2125 self
.assertIn('ip6tnl external ', output
)
2126 output
= check_output('ip -d link show ip6tnl-slaac')
2128 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2130 output
= check_output('ip -6 address show veth99')
2132 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2134 output
= check_output('ip -4 route show default')
2136 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2138 def test_sit_tunnel(self
):
2139 copy_network_unit('12-dummy.netdev', '25-sit.network',
2140 '25-sit-tunnel.netdev', '25-tunnel.network',
2141 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2142 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2143 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2145 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
2147 output
= check_output('ip -d link show sittun99')
2149 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2150 output
= check_output('ip -d link show sittun98')
2152 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2153 output
= check_output('ip -d link show sittun97')
2155 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2156 output
= check_output('ip -d link show sittun96')
2158 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2160 def test_isatap_tunnel(self
):
2161 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2162 '25-isatap-tunnel.netdev', '25-tunnel.network')
2164 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
2166 output
= check_output('ip -d link show isataptun99')
2168 self
.assertRegex(output
, "isatap ")
2170 def test_6rd_tunnel(self
):
2171 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2172 '25-6rd-tunnel.netdev', '25-tunnel.network')
2174 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
2176 output
= check_output('ip -d link show sittun99')
2178 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2180 @expectedFailureIfERSPANv0IsNotSupported()
2181 def test_erspan_tunnel_v0(self
):
2182 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2183 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2184 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2186 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2188 output
= check_output('ip -d link show erspan99')
2190 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2191 self
.assertIn('erspan_ver 0', output
)
2192 self
.assertNotIn('erspan_index 123', output
)
2193 self
.assertNotIn('erspan_dir ingress', output
)
2194 self
.assertNotIn('erspan_hwid 1f', output
)
2195 self
.assertIn('ikey 0.0.0.101', output
)
2196 self
.assertIn('iseq', output
)
2197 self
.assertIn('nopmtudisc', output
)
2198 self
.assertIn('ignore-df', output
)
2199 output
= check_output('ip -d link show erspan98')
2201 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2202 self
.assertIn('erspan_ver 0', output
)
2203 self
.assertNotIn('erspan_index 124', output
)
2204 self
.assertNotIn('erspan_dir egress', output
)
2205 self
.assertNotIn('erspan_hwid 2f', output
)
2206 self
.assertIn('ikey 0.0.0.102', output
)
2207 self
.assertIn('iseq', output
)
2209 def test_erspan_tunnel_v1(self
):
2210 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2211 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2212 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2214 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2216 output
= check_output('ip -d link show erspan99')
2218 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2219 self
.assertIn('erspan_ver 1', output
)
2220 self
.assertIn('erspan_index 123', output
)
2221 self
.assertNotIn('erspan_dir ingress', output
)
2222 self
.assertNotIn('erspan_hwid 1f', output
)
2223 self
.assertIn('ikey 0.0.0.101', output
)
2224 self
.assertIn('okey 0.0.0.101', output
)
2225 self
.assertIn('iseq', output
)
2226 self
.assertIn('oseq', output
)
2227 output
= check_output('ip -d link show erspan98')
2229 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2230 self
.assertIn('erspan_ver 1', output
)
2231 self
.assertIn('erspan_index 124', output
)
2232 self
.assertNotIn('erspan_dir egress', output
)
2233 self
.assertNotIn('erspan_hwid 2f', output
)
2234 self
.assertIn('ikey 0.0.0.102', output
)
2235 self
.assertIn('okey 0.0.0.102', output
)
2236 self
.assertIn('iseq', output
)
2237 self
.assertIn('oseq', output
)
2239 @expectedFailureIfERSPANv2IsNotSupported()
2240 def test_erspan_tunnel_v2(self
):
2241 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2242 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2243 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2245 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
2247 output
= check_output('ip -d link show erspan99')
2249 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2250 self
.assertIn('erspan_ver 2', output
)
2251 self
.assertNotIn('erspan_index 123', output
)
2252 self
.assertIn('erspan_dir ingress', output
)
2253 self
.assertIn('erspan_hwid 0x1f', output
)
2254 self
.assertIn('ikey 0.0.0.101', output
)
2255 self
.assertIn('okey 0.0.0.101', output
)
2256 self
.assertIn('iseq', output
)
2257 self
.assertIn('oseq', output
)
2258 output
= check_output('ip -d link show erspan98')
2260 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2261 self
.assertIn('erspan_ver 2', output
)
2262 self
.assertNotIn('erspan_index 124', output
)
2263 self
.assertIn('erspan_dir egress', output
)
2264 self
.assertIn('erspan_hwid 0x2f', output
)
2265 self
.assertIn('ikey 0.0.0.102', output
)
2266 self
.assertIn('okey 0.0.0.102', output
)
2267 self
.assertIn('iseq', output
)
2268 self
.assertIn('oseq', output
)
2270 def test_tunnel_independent(self
):
2271 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2274 self
.wait_online(['ipiptun99:carrier'])
2276 def test_tunnel_independent_loopback(self
):
2277 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2280 self
.wait_online(['ipiptun99:carrier'])
2282 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2283 def test_xfrm(self
):
2284 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2285 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2286 '26-netdev-link-local-addressing-yes.network')
2289 self
.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
2291 output
= check_output('ip -d link show dev xfrm98')
2293 self
.assertIn('xfrm98@dummy98:', output
)
2294 self
.assertIn('xfrm if_id 0x98 ', output
)
2296 output
= check_output('ip -d link show dev xfrm99')
2298 self
.assertIn('xfrm99@lo:', output
)
2299 self
.assertIn('xfrm if_id 0x99 ', output
)
2301 @expectedFailureIfModuleIsNotAvailable('fou')
2303 # The following redundant check is necessary for CentOS CI.
2304 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2305 self
.assertTrue(is_module_available('fou'))
2307 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2308 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2309 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2312 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
2314 output
= check_output('ip fou show')
2316 self
.assertRegex(output
, 'port 55555 ipproto 4')
2317 self
.assertRegex(output
, 'port 55556 ipproto 47')
2319 output
= check_output('ip -d link show ipiptun96')
2321 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2322 output
= check_output('ip -d link show sittun96')
2324 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2325 output
= check_output('ip -d link show gretun96')
2327 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2328 output
= check_output('ip -d link show gretap96')
2330 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2332 def test_vxlan(self
):
2333 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2334 '25-vxlan.netdev', '25-vxlan.network',
2335 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2336 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2337 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2338 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2341 self
.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2342 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
2344 output
= check_output('ip -d -d link show vxlan99')
2346 self
.assertIn('999', output
)
2347 self
.assertIn('5555', output
)
2348 self
.assertIn('l2miss', output
)
2349 self
.assertIn('l3miss', output
)
2350 self
.assertIn('gbp', output
)
2351 # Since [0] some of the options use slightly different names and some
2352 # options with default values are shown only if the -d(etails) setting
2354 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2355 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2356 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2357 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2358 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2359 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2361 output
= check_output('bridge fdb show dev vxlan99')
2363 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2364 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2365 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2367 output
= networkctl_status('vxlan99')
2369 self
.assertIn('VNI: 999', output
)
2370 self
.assertIn('Destination Port: 5555', output
)
2371 self
.assertIn('Underlying Device: test1', output
)
2373 output
= check_output('bridge fdb show dev vxlan97')
2375 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2376 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2377 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2379 output
= check_output('ip -d link show vxlan-slaac')
2381 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2383 output
= check_output('ip -6 address show veth99')
2385 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2387 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2388 def test_macsec(self
):
2389 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2390 '26-macsec.network', '12-dummy.netdev')
2393 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
2395 output
= check_output('ip -d link show macsec99')
2397 self
.assertRegex(output
, 'macsec99@dummy98')
2398 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2399 self
.assertRegex(output
, 'encrypt on')
2401 output
= check_output('ip macsec show macsec99')
2403 self
.assertRegex(output
, 'encrypt on')
2404 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2405 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2406 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2407 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2408 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2409 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2410 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2411 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2412 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2413 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2414 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2416 def test_nlmon(self
):
2417 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2420 self
.wait_online(['nlmon99:carrier'])
2422 @expectedFailureIfModuleIsNotAvailable('ifb')
2424 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2427 self
.wait_online(['ifb99:degraded'])
2429 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2437 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2438 def test_l2tp_udp(self
):
2439 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2440 '25-l2tp-udp.netdev', '25-l2tp.network')
2443 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2445 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2447 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2448 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2449 self
.assertRegex(output
, "Peer tunnel 11")
2450 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2451 self
.assertRegex(output
, "UDP checksum: enabled")
2453 output
= check_output('ip l2tp show session tid 10 session_id 15')
2455 self
.assertRegex(output
, "Session 15 in tunnel 10")
2456 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2457 self
.assertRegex(output
, "interface name: l2tp-ses1")
2459 output
= check_output('ip l2tp show session tid 10 session_id 17')
2461 self
.assertRegex(output
, "Session 17 in tunnel 10")
2462 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2463 self
.assertRegex(output
, "interface name: l2tp-ses2")
2465 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2466 def test_l2tp_ip(self
):
2467 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2468 '25-l2tp-ip.netdev', '25-l2tp.network')
2471 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2473 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2475 self
.assertRegex(output
, "Tunnel 10, encap IP")
2476 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2477 self
.assertRegex(output
, "Peer tunnel 12")
2479 output
= check_output('ip l2tp show session tid 10 session_id 25')
2481 self
.assertRegex(output
, "Session 25 in tunnel 10")
2482 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2483 self
.assertRegex(output
, "interface name: l2tp-ses3")
2485 output
= check_output('ip l2tp show session tid 10 session_id 27')
2487 self
.assertRegex(output
, "Session 27 in tunnel 10")
2488 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2489 self
.assertRegex(output
, "interface name: l2tp-ses4")
2491 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2499 def verify_address_static(
2529 output
= check_output('ip address show dev dummy98')
2533 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2534 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2535 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2536 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2537 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2538 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2541 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2542 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2543 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2546 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2547 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2548 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2551 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2552 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2553 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2554 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2555 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2556 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2559 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2560 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2563 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2564 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2565 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2566 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2569 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2570 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2572 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2574 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2576 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2578 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2581 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2582 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2583 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2584 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2587 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2588 prefix16
= ip4_null_16
[:-len('.0.1')]
2589 self
.assertTrue(ip4_null_24
.endswith('.1'))
2590 prefix24
= ip4_null_24
[:-len('.1')]
2591 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2592 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2593 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2594 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2597 self
.assertNotIn('10.4.4.1', output
)
2598 self
.assertNotIn('10.5.4.1', output
)
2599 self
.assertNotIn('10.5.5.1', output
)
2600 self
.assertNotIn('10.8.2.1', output
)
2601 self
.assertNotIn('10.9.3.1', output
)
2602 self
.assertNotIn('2001:db8:0:f101::2', output
)
2603 self
.assertNotIn('2001:db8:0:f103::4', output
)
2606 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2608 check_json(networkctl_json())
2610 def test_address_static(self
):
2611 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2612 self
.setup_nftset('addr4', 'ipv4_addr')
2613 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2614 self
.setup_nftset('ifindex', 'iface_index')
2617 self
.wait_online(['dummy98:routable'])
2621 output
= check_output('ip -4 --json address show dev dummy98')
2622 for i
in json
.loads(output
)[0]['addr_info']:
2623 if i
['label'] == 'subnet16':
2624 ip4_null_16
= i
['local']
2625 elif i
['label'] == 'subnet24':
2626 ip4_null_24
= i
['local']
2627 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2628 self
.assertTrue(ip4_null_24
.endswith('.1'))
2632 output
= check_output('ip -6 --json address show dev dummy98')
2633 for i
in json
.loads(output
)[0]['addr_info']:
2634 if i
['prefixlen'] == 73:
2635 ip6_null_73
= i
['local']
2636 elif i
['prefixlen'] == 74:
2637 ip6_null_74
= i
['local']
2638 self
.assertTrue(ip6_null_73
.endswith(':1'))
2639 self
.assertTrue(ip6_null_74
.endswith(':1'))
2641 self
.verify_address_static(
2646 broadcast2
=' brd 10.4.2.255',
2647 broadcast3
=' brd 10.4.3.63',
2648 peer1
=' peer 10.5.1.101/24',
2649 peer2
=' peer 10.5.2.101/24',
2650 peer3
='/24 brd 10.5.3.255',
2651 peer4
=' peer 2001:db8:0:f103::101/128',
2652 peer5
=' peer 2001:db8:0:f103::102/128',
2657 deprecated2
=' deprecated',
2659 deprecated4
=' deprecated',
2661 flag1
=' noprefixroute',
2663 flag3
=' noprefixroute',
2664 flag4
=' home mngtmpaddr',
2665 ip4_null_16
=ip4_null_16
,
2666 ip4_null_24
=ip4_null_24
,
2667 ip6_null_73
=ip6_null_73
,
2668 ip6_null_74
=ip6_null_74
,
2671 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2672 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2673 self
.check_nftset('ifindex', 'dummy98')
2675 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2677 copy_network_unit('25-address-static.network.d/10-override.conf')
2679 self
.wait_online(['dummy98:routable'])
2680 self
.verify_address_static(
2681 label1
='new-label1',
2683 label3
='new-label3',
2684 broadcast1
=' brd 10.4.1.255',
2686 broadcast3
=' brd 10.4.3.31',
2687 peer1
=' peer 10.5.1.102/24',
2688 peer2
='/24 brd 10.5.2.255',
2689 peer3
=' peer 10.5.3.102/24',
2690 peer4
=' peer 2001:db8:0:f103::201/128',
2692 peer6
=' peer 2001:db8:0:f103::203/128',
2695 deprecated1
=' deprecated',
2697 deprecated3
=' deprecated',
2701 flag2
=' noprefixroute',
2702 flag3
=' home mngtmpaddr',
2703 flag4
=' noprefixroute',
2704 ip4_null_16
=ip4_null_16
,
2705 ip4_null_24
=ip4_null_24
,
2706 ip6_null_73
=ip6_null_73
,
2707 ip6_null_74
=ip6_null_74
,
2710 networkctl_reconfigure('dummy98')
2711 self
.wait_online(['dummy98:routable'])
2712 self
.verify_address_static(
2713 label1
='new-label1',
2715 label3
='new-label3',
2716 broadcast1
=' brd 10.4.1.255',
2718 broadcast3
=' brd 10.4.3.31',
2719 peer1
=' peer 10.5.1.102/24',
2720 peer2
='/24 brd 10.5.2.255',
2721 peer3
=' peer 10.5.3.102/24',
2722 peer4
=' peer 2001:db8:0:f103::201/128',
2724 peer6
=' peer 2001:db8:0:f103::203/128',
2727 deprecated1
=' deprecated',
2729 deprecated3
=' deprecated',
2733 flag2
=' noprefixroute',
2734 flag3
=' home mngtmpaddr',
2735 flag4
=' noprefixroute',
2736 ip4_null_16
=ip4_null_16
,
2737 ip4_null_24
=ip4_null_24
,
2738 ip6_null_73
=ip6_null_73
,
2739 ip6_null_74
=ip6_null_74
,
2743 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2744 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2745 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2746 output
= check_output('ip address show dev dummy98')
2748 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2749 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2751 # 2. reconfigure the interface, and check the deprecated flag is set again
2752 networkctl_reconfigure('dummy98')
2753 self
.wait_online(['dummy98:routable'])
2754 self
.verify_address_static(
2755 label1
='new-label1',
2757 label3
='new-label3',
2758 broadcast1
=' brd 10.4.1.255',
2760 broadcast3
=' brd 10.4.3.31',
2761 peer1
=' peer 10.5.1.102/24',
2762 peer2
='/24 brd 10.5.2.255',
2763 peer3
=' peer 10.5.3.102/24',
2764 peer4
=' peer 2001:db8:0:f103::201/128',
2766 peer6
=' peer 2001:db8:0:f103::203/128',
2769 deprecated1
=' deprecated',
2771 deprecated3
=' deprecated',
2775 flag2
=' noprefixroute',
2776 flag3
=' home mngtmpaddr',
2777 flag4
=' noprefixroute',
2778 ip4_null_16
=ip4_null_16
,
2779 ip4_null_24
=ip4_null_24
,
2780 ip6_null_73
=ip6_null_73
,
2781 ip6_null_74
=ip6_null_74
,
2784 # test for ENOBUFS issue #17012 (with reload)
2785 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2787 self
.wait_online(['dummy98:routable'])
2788 output
= check_output('ip -4 address show dev dummy98')
2789 for i
in range(1, 254):
2790 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2792 # (with reconfigure)
2793 networkctl_reconfigure('dummy98')
2794 self
.wait_online(['dummy98:routable'])
2795 output
= check_output('ip -4 address show dev dummy98')
2796 for i
in range(1, 254):
2797 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2799 # test for an empty string assignment for Address= in [Network]
2800 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2802 self
.wait_online(['dummy98:routable'])
2803 output
= check_output('ip -4 address show dev dummy98')
2804 for i
in range(1, 254):
2805 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2806 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2808 def test_address_ipv4acd(self
):
2809 check_output('ip netns add ns99')
2810 check_output('ip link add veth99 type veth peer veth-peer')
2811 check_output('ip link set veth-peer netns ns99')
2812 check_output('ip link set veth99 up')
2813 check_output('ip netns exec ns99 ip link set veth-peer up')
2814 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2816 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2818 self
.wait_online(['veth99:routable'])
2820 output
= check_output('ip -4 address show dev veth99')
2822 self
.assertNotIn('192.168.100.10/24', output
)
2823 self
.assertIn('192.168.100.11/24', output
)
2825 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2827 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2829 output
= check_output('ip -4 address show dev veth99')
2831 self
.assertNotIn('192.168.100.10/24', output
)
2832 self
.assertIn('192.168.100.11/24', output
)
2834 def test_address_peer_ipv4(self
):
2835 # test for issue #17304
2836 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2838 for trial
in range(2):
2844 self
.wait_online(['dummy98:routable'])
2846 output
= check_output('ip -4 address show dev dummy98')
2847 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2849 @expectedFailureIfModuleIsNotAvailable('vrf')
2850 def test_prefix_route(self
):
2851 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2852 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2853 '25-vrf.netdev', '25-vrf.network')
2854 for trial
in range(2):
2860 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2862 output
= check_output('ip route show table 42 dev dummy98')
2863 print('### ip route show table 42 dev dummy98')
2865 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2866 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2867 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2868 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2869 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2870 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2871 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2872 output
= check_output('ip -6 route show table 42 dev dummy98')
2873 print('### ip -6 route show table 42 dev dummy98')
2877 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2878 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2879 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2880 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2881 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2882 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2883 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2884 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2888 output
= check_output('ip route show dev test1')
2889 print('### ip route show dev test1')
2891 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2892 output
= check_output('ip route show table local dev test1')
2893 print('### ip route show table local dev test1')
2895 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2896 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2897 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2898 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2899 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2900 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2901 output
= check_output('ip -6 route show dev test1')
2902 print('### ip -6 route show dev test1')
2904 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2905 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2906 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2907 output
= check_output('ip -6 route show table local dev test1')
2908 print('### ip -6 route show table local dev test1')
2910 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2911 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2912 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2913 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2914 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2916 def test_configure_without_carrier(self
):
2917 copy_network_unit('11-dummy.netdev')
2919 self
.wait_operstate('test1', 'off', '')
2920 check_output('ip link set dev test1 up carrier off')
2922 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2924 self
.wait_online(['test1:no-carrier'])
2926 carrier_map
= {'on': '1', 'off': '0'}
2927 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2928 for carrier
in ['off', 'on', 'off']:
2929 with self
.subTest(carrier
=carrier
):
2930 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2931 check_output(f
'ip link set dev test1 carrier {carrier}')
2932 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2934 output
= networkctl_status('test1')
2936 self
.assertRegex(output
, '192.168.0.15')
2937 self
.assertRegex(output
, '192.168.0.1')
2938 self
.assertRegex(output
, routable_map
[carrier
])
2940 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2941 copy_network_unit('11-dummy.netdev')
2943 self
.wait_operstate('test1', 'off', '')
2944 check_output('ip link set dev test1 up carrier off')
2946 copy_network_unit('25-test1.network')
2948 self
.wait_online(['test1:no-carrier'])
2950 carrier_map
= {'on': '1', 'off': '0'}
2951 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2952 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2953 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2954 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2955 check_output(f
'ip link set dev test1 carrier {carrier}')
2956 self
.wait_online([f
'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2958 output
= networkctl_status('test1')
2961 self
.assertRegex(output
, '192.168.0.15')
2962 self
.assertRegex(output
, '192.168.0.1')
2964 self
.assertNotRegex(output
, '192.168.0.15')
2965 self
.assertNotRegex(output
, '192.168.0.1')
2966 self
.assertRegex(output
, routable_map
[carrier
])
2968 def test_routing_policy_rule(self
):
2969 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2971 self
.wait_online(['test1:degraded'])
2973 output
= check_output('ip rule list iif test1 priority 111')
2975 self
.assertRegex(output
, '111:')
2976 self
.assertRegex(output
, 'from 192.168.100.18')
2977 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2978 self
.assertRegex(output
, 'iif test1')
2979 self
.assertRegex(output
, 'oif test1')
2980 self
.assertRegex(output
, 'lookup 7')
2982 output
= check_output('ip rule list iif test1 priority 101')
2984 self
.assertRegex(output
, '101:')
2985 self
.assertRegex(output
, 'from all')
2986 self
.assertRegex(output
, 'iif test1')
2987 self
.assertRegex(output
, 'lookup 9')
2989 output
= check_output('ip -6 rule list iif test1 priority 100')
2991 self
.assertRegex(output
, '100:')
2992 self
.assertRegex(output
, 'from all')
2993 self
.assertRegex(output
, 'iif test1')
2994 self
.assertRegex(output
, 'lookup 8')
2996 output
= check_output('ip rule list iif test1 priority 102')
2998 self
.assertRegex(output
, '102:')
2999 self
.assertRegex(output
, 'from 0.0.0.0/8')
3000 self
.assertRegex(output
, 'iif test1')
3001 self
.assertRegex(output
, 'lookup 10')
3003 check_json(networkctl_json())
3005 def test_routing_policy_rule_issue_11280(self
):
3006 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3007 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3009 for trial
in range(3):
3010 restart_networkd(show_logs
=(trial
> 0))
3011 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
3013 output
= check_output('ip rule list table 7')
3015 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3017 output
= check_output('ip rule list table 8')
3019 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3021 def test_routing_policy_rule_reconfigure(self
):
3022 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3024 self
.wait_online(['test1:degraded'])
3026 output
= check_output('ip rule list table 1011')
3028 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3029 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3030 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3031 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3033 output
= check_output('ip -6 rule list table 1011')
3035 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3037 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3039 self
.wait_online(['test1:degraded'])
3041 output
= check_output('ip rule list table 1011')
3043 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3044 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3045 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3046 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3048 output
= check_output('ip -6 rule list table 1011')
3050 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3051 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3053 call('ip rule delete priority 10111')
3054 call('ip rule delete priority 10112')
3055 call('ip rule delete priority 10113')
3056 call('ip rule delete priority 10114')
3057 call('ip -6 rule delete priority 10113')
3059 output
= check_output('ip rule list table 1011')
3061 self
.assertEqual(output
, '')
3063 output
= check_output('ip -6 rule list table 1011')
3065 self
.assertEqual(output
, '')
3067 networkctl_reconfigure('test1')
3068 self
.wait_online(['test1:degraded'])
3070 output
= check_output('ip rule list table 1011')
3072 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3073 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3074 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3075 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3077 output
= check_output('ip -6 rule list table 1011')
3079 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3081 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3082 def test_routing_policy_rule_port_range(self
):
3083 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3085 self
.wait_online(['test1:degraded'])
3087 output
= check_output('ip rule')
3089 self
.assertRegex(output
, '111')
3090 self
.assertRegex(output
, 'from 192.168.100.18')
3091 self
.assertRegex(output
, '1123-1150')
3092 self
.assertRegex(output
, '3224-3290')
3093 self
.assertRegex(output
, 'tcp')
3094 self
.assertRegex(output
, 'lookup 7')
3096 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3097 def test_routing_policy_rule_invert(self
):
3098 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3100 self
.wait_online(['test1:degraded'])
3102 output
= check_output('ip rule')
3104 self
.assertRegex(output
, '111')
3105 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3106 self
.assertRegex(output
, 'tcp')
3107 self
.assertRegex(output
, 'lookup 7')
3109 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3110 def test_routing_policy_rule_uidrange(self
):
3111 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3113 self
.wait_online(['test1:degraded'])
3115 output
= check_output('ip rule')
3117 self
.assertRegex(output
, '111')
3118 self
.assertRegex(output
, 'from 192.168.100.18')
3119 self
.assertRegex(output
, 'lookup 7')
3120 self
.assertRegex(output
, 'uidrange 100-200')
3122 def _test_route_static(self
, manage_foreign_routes
):
3123 if not manage_foreign_routes
:
3124 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3126 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3127 '25-route-static-test1.network', '11-dummy.netdev')
3129 self
.wait_online(['dummy98:routable'])
3131 output
= networkctl_status('dummy98')
3134 print('### ip -6 route show dev dummy98')
3135 output
= check_output('ip -6 route show dev dummy98')
3137 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3138 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3139 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3141 print('### ip -6 route show default')
3142 output
= check_output('ip -6 route show default')
3144 self
.assertIn('default', output
)
3145 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3147 print('### ip -4 route show dev dummy98')
3148 output
= check_output('ip -4 route show dev dummy98')
3150 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3151 self
.assertIn('149.10.124.64 proto static scope link', output
)
3152 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3153 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3154 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3155 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3156 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3157 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3159 print('### ip -4 route show dev dummy98 default')
3160 output
= check_output('ip -4 route show dev dummy98 default')
3162 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3163 self
.assertIn('default via 149.10.124.64 proto static', output
)
3164 self
.assertIn('default proto static', output
)
3165 self
.assertIn('default via 1.1.8.104 proto static', output
)
3167 print('### ip -4 route show table local dev dummy98')
3168 output
= check_output('ip -4 route show table local dev dummy98')
3170 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3171 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3172 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3174 print('### ip -4 route show type blackhole')
3175 output
= check_output('ip -4 route show type blackhole')
3177 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3179 print('### ip -4 route show type unreachable')
3180 output
= check_output('ip -4 route show type unreachable')
3182 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3184 print('### ip -4 route show type prohibit')
3185 output
= check_output('ip -4 route show type prohibit')
3187 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3189 print('### ip -6 route show type blackhole')
3190 output
= check_output('ip -6 route show type blackhole')
3192 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3194 print('### ip -6 route show type unreachable')
3195 output
= check_output('ip -6 route show type unreachable')
3197 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3199 print('### ip -6 route show type prohibit')
3200 output
= check_output('ip -6 route show type prohibit')
3202 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3204 print('### ip route show 192.168.10.1')
3205 output
= check_output('ip route show 192.168.10.1')
3207 self
.assertIn('192.168.10.1 proto static', output
)
3208 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3209 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3210 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3211 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3213 print('### ip route show 192.168.10.2')
3214 output
= check_output('ip route show 192.168.10.2')
3216 # old ip command does not show IPv6 gateways...
3217 self
.assertIn('192.168.10.2 proto static', output
)
3218 self
.assertIn('nexthop', output
)
3219 self
.assertIn('dev test1 weight 20', output
)
3220 self
.assertIn('dev test1 weight 30', output
)
3221 self
.assertIn('dev dummy98 weight 10', output
)
3222 self
.assertIn('dev dummy98 weight 5', output
)
3224 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3225 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3227 # old ip command does not show 'nexthop' keyword and weight...
3228 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3229 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3230 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3231 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3232 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3234 check_json(networkctl_json())
3236 copy_network_unit('25-address-static.network', copy_dropins
=False)
3238 self
.wait_online(['dummy98:routable'])
3240 # check all routes managed by Manager are removed
3241 print('### ip -4 route show type blackhole')
3242 output
= check_output('ip -4 route show type blackhole')
3244 self
.assertEqual(output
, '')
3246 print('### ip -4 route show type unreachable')
3247 output
= check_output('ip -4 route show type unreachable')
3249 self
.assertEqual(output
, '')
3251 print('### ip -4 route show type prohibit')
3252 output
= check_output('ip -4 route show type prohibit')
3254 self
.assertEqual(output
, '')
3256 print('### ip -6 route show type blackhole')
3257 output
= check_output('ip -6 route show type blackhole')
3259 self
.assertEqual(output
, '')
3261 print('### ip -6 route show type unreachable')
3262 output
= check_output('ip -6 route show type unreachable')
3264 self
.assertEqual(output
, '')
3266 print('### ip -6 route show type prohibit')
3267 output
= check_output('ip -6 route show type prohibit')
3269 self
.assertEqual(output
, '')
3271 remove_network_unit('25-address-static.network')
3273 self
.wait_online(['dummy98:routable'])
3275 # check all routes managed by Manager are reconfigured
3276 print('### ip -4 route show type blackhole')
3277 output
= check_output('ip -4 route show type blackhole')
3279 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3281 print('### ip -4 route show type unreachable')
3282 output
= check_output('ip -4 route show type unreachable')
3284 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3286 print('### ip -4 route show type prohibit')
3287 output
= check_output('ip -4 route show type prohibit')
3289 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3291 print('### ip -6 route show type blackhole')
3292 output
= check_output('ip -6 route show type blackhole')
3294 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3296 print('### ip -6 route show type unreachable')
3297 output
= check_output('ip -6 route show type unreachable')
3299 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3301 print('### ip -6 route show type prohibit')
3302 output
= check_output('ip -6 route show type prohibit')
3304 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3306 remove_link('dummy98')
3309 # check all routes managed by Manager are removed
3310 print('### ip -4 route show type blackhole')
3311 output
= check_output('ip -4 route show type blackhole')
3313 self
.assertEqual(output
, '')
3315 print('### ip -4 route show type unreachable')
3316 output
= check_output('ip -4 route show type unreachable')
3318 self
.assertEqual(output
, '')
3320 print('### ip -4 route show type prohibit')
3321 output
= check_output('ip -4 route show type prohibit')
3323 self
.assertEqual(output
, '')
3325 print('### ip -6 route show type blackhole')
3326 output
= check_output('ip -6 route show type blackhole')
3328 self
.assertEqual(output
, '')
3330 print('### ip -6 route show type unreachable')
3331 output
= check_output('ip -6 route show type unreachable')
3333 self
.assertEqual(output
, '')
3335 print('### ip -6 route show type prohibit')
3336 output
= check_output('ip -6 route show type prohibit')
3338 self
.assertEqual(output
, '')
3340 def test_route_static(self
):
3342 for manage_foreign_routes
in [True, False]:
3348 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3349 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3350 self
._test
_route
_static
(manage_foreign_routes
)
3352 @expectedFailureIfRTA_VIAIsNotSupported()
3353 def test_route_via_ipv6(self
):
3354 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3356 self
.wait_online(['dummy98:routable'])
3358 output
= networkctl_status('dummy98')
3361 print('### ip -6 route show dev dummy98')
3362 output
= check_output('ip -6 route show dev dummy98')
3364 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3365 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3367 print('### ip -4 route show dev dummy98')
3368 output
= check_output('ip -4 route show dev dummy98')
3370 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3371 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3373 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3374 def test_route_congctl(self
):
3375 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3377 self
.wait_online(['dummy98:routable'])
3379 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3380 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3382 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3383 self
.assertIn('congctl dctcp', output
)
3385 print('### ip -4 route show dev dummy98 149.10.124.66')
3386 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3388 self
.assertIn('149.10.124.66 proto static', output
)
3389 self
.assertIn('congctl dctcp', output
)
3390 self
.assertIn('rto_min 300s', output
)
3392 @expectedFailureIfModuleIsNotAvailable('vrf')
3393 def test_route_vrf(self
):
3394 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3395 '25-vrf.netdev', '25-vrf.network')
3397 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
3399 output
= check_output('ip route show vrf vrf99')
3401 self
.assertRegex(output
, 'default via 192.168.100.1')
3403 output
= check_output('ip route show')
3405 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3407 def test_gateway_reconfigure(self
):
3408 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3410 self
.wait_online(['dummy98:routable'])
3411 print('### ip -4 route show dev dummy98 default')
3412 output
= check_output('ip -4 route show dev dummy98 default')
3414 self
.assertIn('default via 149.10.124.59 proto static', output
)
3415 self
.assertNotIn('149.10.124.60', output
)
3417 remove_network_unit('25-gateway-static.network')
3418 copy_network_unit('25-gateway-next-static.network')
3420 self
.wait_online(['dummy98:routable'])
3421 print('### ip -4 route show dev dummy98 default')
3422 output
= check_output('ip -4 route show dev dummy98 default')
3424 self
.assertNotIn('149.10.124.59', output
)
3425 self
.assertIn('default via 149.10.124.60 proto static', output
)
3427 def test_ip_route_ipv6_src_route(self
):
3428 # a dummy device does not make the addresses go through tentative state, so we
3429 # reuse a bond from an earlier test, which does make the addresses go through
3430 # tentative state, and do our test on that
3431 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3433 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
3435 output
= check_output('ip -6 route list dev bond199')
3437 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3439 def test_route_preferred_source_with_existing_address(self
):
3441 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3446 networkctl_reconfigure('dummy98')
3448 self
.wait_online(['dummy98:routable'])
3450 output
= check_output('ip -6 route list dev dummy98')
3452 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3454 def test_ip_link_mac_address(self
):
3455 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3457 self
.wait_online(['dummy98:degraded'])
3459 output
= check_output('ip link show dummy98')
3461 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3463 def test_ip_link_unmanaged(self
):
3464 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3467 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3469 def test_ipv6_address_label(self
):
3470 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3472 self
.wait_online(['dummy98:degraded'])
3474 output
= check_output('ip addrlabel list')
3476 self
.assertRegex(output
, '2004:da8:1::/64')
3478 def test_ipv6_proxy_ndp(self
):
3479 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3482 self
.wait_online(['dummy98:routable'])
3484 output
= check_output('ip neighbor show proxy dev dummy98')
3486 for i
in range(1, 5):
3487 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3489 def test_neighbor(self
):
3490 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3491 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3492 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3495 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'])
3497 print('### ip neigh list dev gretun97')
3498 output
= check_output('ip neigh list dev gretun97')
3500 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3501 self
.assertNotIn('10.0.0.23', output
)
3503 print('### ip neigh list dev ip6gretun97')
3504 output
= check_output('ip neigh list dev ip6gretun97')
3506 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3507 self
.assertNotIn('2001:db8:0:f102::18', output
)
3509 print('### ip neigh list dev dummy98')
3510 output
= check_output('ip neigh list dev dummy98')
3512 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3513 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3514 self
.assertNotIn('2004:da8:1:0::2', output
)
3515 self
.assertNotIn('192.168.10.2', output
)
3516 self
.assertNotIn('00:00:5e:00:02:67', output
)
3518 check_json(networkctl_json())
3520 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3521 # the valid configurations in 10-step1.conf.
3522 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3524 self
.wait_online(['dummy98:degraded'])
3526 print('### ip neigh list dev dummy98')
3527 output
= check_output('ip neigh list dev dummy98')
3529 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3530 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3531 self
.assertNotIn('2004:da8:1:0::2', output
)
3532 self
.assertNotIn('192.168.10.2', output
)
3533 self
.assertNotIn('00:00:5e:00:02:67', output
)
3535 check_json(networkctl_json())
3537 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3538 '25-neighbor-dummy.network.d/10-step2.conf')
3539 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3541 self
.wait_online(['dummy98:degraded'])
3543 print('### ip neigh list dev dummy98')
3544 output
= check_output('ip neigh list dev dummy98')
3546 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3547 self
.assertNotIn('00:00:5e:00:02:65', output
)
3548 self
.assertNotIn('00:00:5e:00:02:66', output
)
3549 self
.assertNotIn('00:00:5e:00:03:65', output
)
3550 self
.assertNotIn('2004:da8:1::1', output
)
3552 def test_link_local_addressing(self
):
3553 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3554 '25-link-local-addressing-no.network', '12-dummy.netdev')
3556 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
3558 output
= check_output('ip address show dev test1')
3560 self
.assertRegex(output
, 'inet .* scope link')
3561 self
.assertRegex(output
, 'inet6 .* scope link')
3563 output
= check_output('ip address show dev dummy98')
3565 self
.assertNotRegex(output
, 'inet6* .* scope link')
3567 # Documentation/networking/ip-sysctl.txt
3569 # addr_gen_mode - INTEGER
3570 # Defines how link-local and autoconf addresses are generated.
3572 # 0: generate address based on EUI64 (default)
3573 # 1: do no generate a link-local address, use EUI64 for addresses generated
3575 # 2: generate stable privacy addresses, using the secret from
3576 # stable_secret (RFC7217)
3577 # 3: generate stable privacy addresses, using a random secret if unset
3579 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3580 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3581 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3583 def test_link_local_addressing_ipv6ll(self
):
3584 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3586 self
.wait_online(['dummy98:degraded'])
3588 # An IPv6LL address exists by default.
3589 output
= check_output('ip address show dev dummy98')
3591 self
.assertRegex(output
, 'inet6 .* scope link')
3593 copy_network_unit('25-link-local-addressing-no.network')
3595 self
.wait_online(['dummy98:carrier'])
3597 # Check if the IPv6LL address is removed.
3598 output
= check_output('ip address show dev dummy98')
3600 self
.assertNotRegex(output
, 'inet6 .* scope link')
3602 remove_network_unit('25-link-local-addressing-no.network')
3604 self
.wait_online(['dummy98:degraded'])
3606 # Check if a new IPv6LL address is assigned.
3607 output
= check_output('ip address show dev dummy98')
3609 self
.assertRegex(output
, 'inet6 .* scope link')
3611 @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
3612 def test_sysctl(self
):
3613 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3614 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3616 self
.wait_online(['dummy98:degraded'])
3618 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3619 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3620 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3621 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3622 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3623 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3624 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3625 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3626 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3627 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3629 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3631 self
.wait_online(['dummy98:degraded'])
3633 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3635 def test_sysctl_disable_ipv6(self
):
3636 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3638 print('## Disable ipv6')
3639 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3640 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3643 self
.wait_online(['dummy98:routable'])
3645 output
= check_output('ip -4 address show dummy98')
3647 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3648 output
= check_output('ip -6 address show dummy98')
3650 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3651 self
.assertRegex(output
, 'inet6 .* scope link')
3652 output
= check_output('ip -4 route show dev dummy98')
3654 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3655 output
= check_output('ip -6 route show default')
3657 self
.assertRegex(output
, 'default')
3658 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3660 remove_link('dummy98')
3662 print('## Enable ipv6')
3663 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3664 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3667 self
.wait_online(['dummy98:routable'])
3669 output
= check_output('ip -4 address show dummy98')
3671 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3672 output
= check_output('ip -6 address show dummy98')
3674 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3675 self
.assertRegex(output
, 'inet6 .* scope link')
3676 output
= check_output('ip -4 route show dev dummy98')
3678 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3679 output
= check_output('ip -6 route show default')
3681 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3683 def test_bind_carrier(self
):
3684 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3687 # no bound interface.
3688 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3689 output
= check_output('ip address show test1')
3691 self
.assertNotIn('UP,LOWER_UP', output
)
3692 self
.assertIn('DOWN', output
)
3693 self
.assertNotIn('192.168.10', output
)
3695 # add one bound interface. The interface will be up.
3696 check_output('ip link add dummy98 type dummy')
3697 check_output('ip link set dummy98 up')
3698 self
.wait_online(['test1:routable'])
3699 output
= check_output('ip address show test1')
3701 self
.assertIn('UP,LOWER_UP', output
)
3702 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3704 # add another bound interface. The interface is still up.
3705 check_output('ip link add dummy99 type dummy')
3706 check_output('ip link set dummy99 up')
3707 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3708 output
= check_output('ip address show test1')
3710 self
.assertIn('UP,LOWER_UP', output
)
3711 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3713 # remove one of the bound interfaces. The interface is still up
3714 remove_link('dummy98')
3715 output
= check_output('ip address show test1')
3717 self
.assertIn('UP,LOWER_UP', output
)
3718 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3720 # bring down the remaining bound interface. The interface will be down.
3721 check_output('ip link set dummy99 down')
3722 self
.wait_operstate('test1', 'off')
3723 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3724 output
= check_output('ip address show test1')
3726 self
.assertNotIn('UP,LOWER_UP', output
)
3727 self
.assertIn('DOWN', output
)
3728 self
.assertNotIn('192.168.10', output
)
3730 # bring up the bound interface. The interface will be up.
3731 check_output('ip link set dummy99 up')
3732 self
.wait_online(['test1:routable'])
3733 output
= check_output('ip address show test1')
3735 self
.assertIn('UP,LOWER_UP', output
)
3736 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3738 # remove the remaining bound interface. The interface will be down.
3739 remove_link('dummy99')
3740 self
.wait_operstate('test1', 'off')
3741 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3742 output
= check_output('ip address show test1')
3744 self
.assertNotIn('UP,LOWER_UP', output
)
3745 self
.assertIn('DOWN', output
)
3746 self
.assertNotIn('192.168.10', output
)
3748 # re-add one bound interface. The interface will be up.
3749 check_output('ip link add dummy98 type dummy')
3750 check_output('ip link set dummy98 up')
3751 self
.wait_online(['test1:routable'])
3752 output
= check_output('ip address show test1')
3754 self
.assertIn('UP,LOWER_UP', output
)
3755 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3757 def _test_activation_policy(self
, interface
, test
):
3758 conffile
= '25-activation-policy.network'
3760 conffile
= f
'{conffile}.d/{test}.conf'
3761 if interface
== 'vlan99':
3762 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3763 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3766 always
= test
.startswith('always')
3767 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3768 expect_up
= initial_up
3769 next_up
= not expect_up
3771 if test
.endswith('down'):
3772 self
.wait_activated(interface
)
3774 for iteration
in range(4):
3775 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3776 operstate
= 'routable' if expect_up
else 'off'
3777 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3778 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3781 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3782 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3783 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3785 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3788 check_output(f
'ip link set dev {interface} up')
3790 check_output(f
'ip link set dev {interface} down')
3791 expect_up
= initial_up
if always
else next_up
3792 next_up
= not next_up
3796 def test_activation_policy(self
):
3798 for interface
in ['test1', 'vlan99']:
3799 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3805 print(f
'### test_activation_policy(interface={interface}, test={test})')
3806 with self
.subTest(interface
=interface
, test
=test
):
3807 self
._test
_activation
_policy
(interface
, test
)
3809 def _test_activation_policy_required_for_online(self
, policy
, required
):
3810 conffile
= '25-activation-policy.network'
3811 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3813 units
+= [f
'{conffile}.d/{policy}.conf']
3815 units
+= [f
'{conffile}.d/required-{required}.conf']
3816 copy_network_unit(*units
, copy_dropins
=False)
3819 if policy
.endswith('down'):
3820 self
.wait_activated('test1')
3822 if policy
.endswith('down') or policy
== 'manual':
3823 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3825 self
.wait_online(['test1'])
3827 if policy
== 'always-down':
3828 # if always-down, required for online is forced to no
3831 # otherwise if required for online is specified, it should match that
3832 expected
= required
== 'yes'
3834 # otherwise if only policy specified, required for online defaults to
3835 # true if policy is up, always-up, or bound
3836 expected
= policy
.endswith('up') or policy
== 'bound'
3838 # default is true, if neither are specified
3841 output
= networkctl_status('test1')
3844 yesno
= 'yes' if expected
else 'no'
3845 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3847 def test_activation_policy_required_for_online(self
):
3849 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3850 for required
in ['yes', 'no', '']:
3856 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3857 with self
.subTest(policy
=policy
, required
=required
):
3858 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3860 def test_domain(self
):
3861 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3863 self
.wait_online(['dummy98:routable'])
3865 output
= networkctl_status('dummy98')
3867 self
.assertRegex(output
, 'Address: 192.168.42.100')
3868 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3869 self
.assertRegex(output
, 'Search Domains: one')
3871 def test_keep_configuration_static(self
):
3872 check_output('ip link add name dummy98 type dummy')
3873 check_output('ip address add 10.1.2.3/16 dev dummy98')
3874 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3875 output
= check_output('ip address show dummy98')
3877 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3878 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3879 output
= check_output('ip route show dev dummy98')
3882 copy_network_unit('24-keep-configuration-static.network')
3884 self
.wait_online(['dummy98:routable'])
3886 output
= check_output('ip address show dummy98')
3888 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3889 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3891 def check_nexthop(self
, manage_foreign_nexthops
, first
):
3892 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3894 output
= check_output('ip nexthop list dev veth99')
3897 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3898 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3900 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
3901 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
3902 self
.assertIn('id 3 dev veth99', output
)
3903 self
.assertIn('id 4 dev veth99', output
)
3905 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3907 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
3908 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
3909 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3910 if manage_foreign_nexthops
:
3911 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3913 output
= check_output('ip nexthop list dev dummy98')
3916 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
3918 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
3919 if manage_foreign_nexthops
:
3920 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
3922 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
3924 # kernel manages blackhole nexthops on lo
3925 output
= check_output('ip nexthop list dev lo')
3928 self
.assertIn('id 6 blackhole', output
)
3929 self
.assertIn('id 7 blackhole', output
)
3931 self
.assertIn('id 1 blackhole', output
)
3932 self
.assertIn('id 2 blackhole', output
)
3934 # group nexthops are shown with -0 option
3936 output
= check_output('ip -0 nexthop list id 21')
3938 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
3940 output
= check_output('ip -0 nexthop list id 20')
3942 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
3944 output
= check_output('ip route show dev veth99 10.10.10.10')
3947 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
3949 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
3951 output
= check_output('ip route show dev veth99 10.10.10.11')
3954 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
3956 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
3958 output
= check_output('ip route show dev veth99 10.10.10.12')
3961 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
3963 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
3965 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3968 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3970 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
3972 output
= check_output('ip route show 10.10.10.13')
3975 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
3977 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
3979 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
3982 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
3984 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
3986 output
= check_output('ip route show 10.10.10.14')
3989 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
3990 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
3992 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
3993 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
3994 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
3996 check_json(networkctl_json())
3998 def _test_nexthop(self
, manage_foreign_nexthops
):
3999 if not manage_foreign_nexthops
:
4000 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4002 check_output('ip link add dummy98 type dummy')
4003 check_output('ip link set dummy98 up')
4004 check_output('ip address add 192.168.20.20/24 dev dummy98')
4005 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4007 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4008 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4011 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4013 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4014 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4016 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4018 remove_network_unit('25-nexthop-2.network')
4019 copy_network_unit('25-nexthop-nothing.network')
4021 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4023 output
= check_output('ip nexthop list dev veth99')
4025 self
.assertEqual(output
, '')
4026 output
= check_output('ip nexthop list dev lo')
4028 self
.assertEqual(output
, '')
4030 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4031 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4032 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4033 # here to test reconfiguring with different .network files does not trigger race.
4034 # See also comments in link_drop_requests().
4035 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4036 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4038 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4040 # Remove nexthop with ID 20
4041 check_output('ip nexthop del id 20')
4042 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4045 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4046 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4047 # hence test1 should be stuck in the configuring state.
4048 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4050 # Wait for a while, and check if the interface is still in the configuring state.
4052 output
= networkctl_status('test1')
4053 self
.assertIn('State: routable (configuring)', output
)
4055 # Reconfigure the interface that has nexthop with ID 20 and 21,
4056 # then the route requested by test1 can be configured.
4057 networkctl_reconfigure('dummy98')
4058 self
.wait_online(['test1:routable'])
4060 # Check if the requested route actually configured.
4061 output
= check_output('ip route show 10.10.11.10')
4063 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4064 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4065 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4067 remove_link('veth99')
4070 output
= check_output('ip nexthop list dev lo')
4072 self
.assertEqual(output
, '')
4074 @expectedFailureIfNexthopIsNotAvailable()
4075 def test_nexthop(self
):
4077 for manage_foreign_nexthops
in [True, False]:
4083 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4084 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4085 self
._test
_nexthop
(manage_foreign_nexthops
)
4087 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4095 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4096 def test_qdisc_cake(self
):
4097 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4099 self
.wait_online(['dummy98:routable'])
4101 output
= check_output('tc qdisc show dev dummy98')
4103 self
.assertIn('qdisc cake 3a: root', output
)
4104 self
.assertIn('bandwidth 500Mbit', output
)
4105 self
.assertIn('autorate-ingress', output
)
4106 self
.assertIn('diffserv8', output
)
4107 self
.assertIn('dual-dsthost', output
)
4108 self
.assertIn(' nat', output
)
4109 self
.assertIn(' wash', output
)
4110 self
.assertIn(' split-gso', output
)
4111 self
.assertIn(' raw', output
)
4112 self
.assertIn(' atm', output
)
4113 self
.assertIn('overhead 128', output
)
4114 self
.assertIn('mpu 20', output
)
4115 self
.assertIn('fwmark 0xff00', output
)
4116 self
.assertIn('rtt 1s', output
)
4117 self
.assertIn('ack-filter-aggressive', output
)
4119 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4120 def test_qdisc_codel(self
):
4121 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4123 self
.wait_online(['dummy98:routable'])
4125 output
= check_output('tc qdisc show dev dummy98')
4127 self
.assertRegex(output
, 'qdisc codel 33: root')
4128 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4130 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4131 def test_qdisc_drr(self
):
4132 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4134 self
.wait_online(['dummy98:routable'])
4136 output
= check_output('tc qdisc show dev dummy98')
4138 self
.assertRegex(output
, 'qdisc drr 2: root')
4139 output
= check_output('tc class show dev dummy98')
4141 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4143 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4144 def test_qdisc_ets(self
):
4145 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4147 self
.wait_online(['dummy98:routable'])
4149 output
= check_output('tc qdisc show dev dummy98')
4152 self
.assertRegex(output
, 'qdisc ets 3a: root')
4153 self
.assertRegex(output
, 'bands 10 strict 3')
4154 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4155 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4157 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4158 def test_qdisc_fq(self
):
4159 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4161 self
.wait_online(['dummy98:routable'])
4163 output
= check_output('tc qdisc show dev dummy98')
4165 self
.assertRegex(output
, 'qdisc fq 32: root')
4166 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4167 self
.assertRegex(output
, 'quantum 1500')
4168 self
.assertRegex(output
, 'initial_quantum 13000')
4169 self
.assertRegex(output
, 'maxrate 1Mbit')
4171 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4172 def test_qdisc_fq_codel(self
):
4173 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4175 self
.wait_online(['dummy98:routable'])
4177 output
= check_output('tc qdisc show dev dummy98')
4179 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4180 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')
4182 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4183 def test_qdisc_fq_pie(self
):
4184 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4186 self
.wait_online(['dummy98:routable'])
4188 output
= check_output('tc qdisc show dev dummy98')
4191 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4192 self
.assertRegex(output
, 'limit 200000p')
4194 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4195 def test_qdisc_gred(self
):
4196 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4198 self
.wait_online(['dummy98:routable'])
4200 output
= check_output('tc qdisc show dev dummy98')
4202 self
.assertRegex(output
, 'qdisc gred 38: root')
4203 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4205 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4206 def test_qdisc_hhf(self
):
4207 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4209 self
.wait_online(['dummy98:routable'])
4211 output
= check_output('tc qdisc show dev dummy98')
4213 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4214 self
.assertRegex(output
, 'limit 1022p')
4216 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4217 def test_qdisc_htb_fifo(self
):
4218 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4220 self
.wait_online(['dummy98:routable'])
4222 output
= check_output('tc qdisc show dev dummy98')
4224 self
.assertRegex(output
, 'qdisc htb 2: root')
4225 self
.assertRegex(output
, r
'default (0x30|30)')
4227 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4228 self
.assertRegex(output
, 'limit 100000p')
4230 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4231 self
.assertRegex(output
, 'limit 1000000')
4233 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4234 self
.assertRegex(output
, 'limit 1023p')
4236 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4238 output
= check_output('tc -d class show dev dummy98')
4240 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4241 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4242 # which is fixed in v6.3.0 by
4243 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4244 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4245 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4246 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4247 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4248 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4249 self
.assertRegex(output
, 'burst 123456')
4250 self
.assertRegex(output
, 'cburst 123457')
4252 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4253 def test_qdisc_ingress(self
):
4254 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4255 '25-qdisc-ingress.network', '11-dummy.netdev')
4257 self
.wait_online(['dummy98:routable', 'test1:routable'])
4259 output
= check_output('tc qdisc show dev dummy98')
4261 self
.assertRegex(output
, 'qdisc clsact')
4263 output
= check_output('tc qdisc show dev test1')
4265 self
.assertRegex(output
, 'qdisc ingress')
4267 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4268 def test_qdisc_netem(self
):
4269 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4270 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4272 self
.wait_online(['dummy98:routable', 'test1:routable'])
4274 output
= check_output('tc qdisc show dev dummy98')
4276 self
.assertRegex(output
, 'qdisc netem 30: root')
4277 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4279 output
= check_output('tc qdisc show dev test1')
4281 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4282 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4284 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4285 def test_qdisc_pie(self
):
4286 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4288 self
.wait_online(['dummy98:routable'])
4290 output
= check_output('tc qdisc show dev dummy98')
4292 self
.assertRegex(output
, 'qdisc pie 3a: root')
4293 self
.assertRegex(output
, 'limit 200000')
4295 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4296 def test_qdisc_qfq(self
):
4297 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4299 self
.wait_online(['dummy98:routable'])
4301 output
= check_output('tc qdisc show dev dummy98')
4303 self
.assertRegex(output
, 'qdisc qfq 2: root')
4304 output
= check_output('tc class show dev dummy98')
4306 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4307 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4309 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4310 def test_qdisc_sfb(self
):
4311 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4313 self
.wait_online(['dummy98:routable'])
4315 output
= check_output('tc qdisc show dev dummy98')
4317 self
.assertRegex(output
, 'qdisc sfb 39: root')
4318 self
.assertRegex(output
, 'limit 200000')
4320 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4321 def test_qdisc_sfq(self
):
4322 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4324 self
.wait_online(['dummy98:routable'])
4326 output
= check_output('tc qdisc show dev dummy98')
4328 self
.assertRegex(output
, 'qdisc sfq 36: root')
4329 self
.assertRegex(output
, 'perturb 5sec')
4331 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4332 def test_qdisc_tbf(self
):
4333 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4335 self
.wait_online(['dummy98:routable'])
4337 output
= check_output('tc qdisc show dev dummy98')
4339 self
.assertRegex(output
, 'qdisc tbf 35: root')
4340 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4342 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4343 def test_qdisc_teql(self
):
4344 call_quiet('rmmod sch_teql')
4346 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4348 self
.wait_links('dummy98')
4349 check_output('modprobe sch_teql max_equalizers=2')
4350 self
.wait_online(['dummy98:routable'])
4352 output
= check_output('tc qdisc show dev dummy98')
4354 self
.assertRegex(output
, 'qdisc teql1 31: root')
4356 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4364 def test_state_file(self
):
4365 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4367 self
.wait_online(['dummy98:routable'])
4369 # make link state file updated
4370 resolvectl('revert', 'dummy98')
4372 check_json(networkctl_json())
4374 output
= read_link_state_file('dummy98')
4376 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4377 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4378 self
.assertIn('ADMIN_STATE=configured', output
)
4379 self
.assertIn('OPER_STATE=routable', output
)
4380 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4381 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4382 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4383 self
.assertIn('ACTIVATION_POLICY=up', output
)
4384 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4385 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4386 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4387 self
.assertIn('DOMAINS=hogehoge', output
)
4388 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4389 self
.assertIn('LLMNR=no', output
)
4390 self
.assertIn('MDNS=yes', output
)
4391 self
.assertIn('DNSSEC=no', output
)
4393 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4394 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4395 resolvectl('llmnr', 'dummy98', 'yes')
4396 resolvectl('mdns', 'dummy98', 'no')
4397 resolvectl('dnssec', 'dummy98', 'yes')
4398 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4400 check_json(networkctl_json())
4402 output
= read_link_state_file('dummy98')
4404 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4405 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4406 self
.assertIn('DOMAINS=hogehogehoge', output
)
4407 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4408 self
.assertIn('LLMNR=yes', output
)
4409 self
.assertIn('MDNS=no', output
)
4410 self
.assertIn('DNSSEC=yes', output
)
4412 timedatectl('revert', 'dummy98')
4414 check_json(networkctl_json())
4416 output
= read_link_state_file('dummy98')
4418 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4419 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4420 self
.assertIn('DOMAINS=hogehogehoge', output
)
4421 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4422 self
.assertIn('LLMNR=yes', output
)
4423 self
.assertIn('MDNS=no', output
)
4424 self
.assertIn('DNSSEC=yes', output
)
4426 resolvectl('revert', 'dummy98')
4428 check_json(networkctl_json())
4430 output
= read_link_state_file('dummy98')
4432 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4433 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4434 self
.assertIn('DOMAINS=hogehoge', output
)
4435 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4436 self
.assertIn('LLMNR=no', output
)
4437 self
.assertIn('MDNS=yes', output
)
4438 self
.assertIn('DNSSEC=no', output
)
4440 def test_address_state(self
):
4441 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4444 self
.wait_online(['dummy98:degraded'])
4446 output
= read_link_state_file('dummy98')
4447 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4448 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4450 # with a routable IPv4 address
4451 check_output('ip address add 10.1.2.3/16 dev dummy98')
4452 self
.wait_online(['dummy98:routable'], ipv4
=True)
4453 self
.wait_online(['dummy98:routable'])
4455 output
= read_link_state_file('dummy98')
4456 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4457 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4459 check_output('ip address del 10.1.2.3/16 dev dummy98')
4461 # with a routable IPv6 address
4462 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4463 self
.wait_online(['dummy98:routable'], ipv6
=True)
4464 self
.wait_online(['dummy98:routable'])
4466 output
= read_link_state_file('dummy98')
4467 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4468 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4470 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4478 def test_bond_keep_master(self
):
4479 check_output('ip link add bond199 type bond mode active-backup')
4480 check_output('ip link add dummy98 type dummy')
4481 check_output('ip link set dummy98 master bond199')
4483 copy_network_unit('23-keep-master.network')
4485 self
.wait_online(['dummy98:enslaved'])
4487 output
= check_output('ip -d link show bond199')
4489 self
.assertRegex(output
, 'active_slave dummy98')
4491 output
= check_output('ip -d link show dummy98')
4493 self
.assertRegex(output
, 'master bond199')
4495 def test_bond_active_slave(self
):
4496 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4498 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4500 output
= check_output('ip -d link show bond199')
4502 self
.assertIn('active_slave dummy98', output
)
4504 def test_bond_primary_slave(self
):
4505 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4507 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4509 output
= check_output('ip -d link show bond199')
4511 self
.assertIn('primary dummy98', output
)
4514 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4515 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4516 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4517 f
.write(f
'[Link]\nMACAddress={mac}\n')
4520 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
4522 output
= check_output('ip -d link show bond199')
4524 self
.assertIn(f
'link/ether {mac}', output
)
4526 def test_bond_operstate(self
):
4527 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4528 '25-bond99.network', '25-bond-slave.network')
4530 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
4532 output
= check_output('ip -d link show dummy98')
4534 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4536 output
= check_output('ip -d link show test1')
4538 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4540 output
= check_output('ip -d link show bond99')
4542 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4544 self
.wait_operstate('dummy98', 'enslaved')
4545 self
.wait_operstate('test1', 'enslaved')
4546 self
.wait_operstate('bond99', 'routable')
4548 check_output('ip link set dummy98 down')
4550 self
.wait_operstate('dummy98', 'off')
4551 self
.wait_operstate('test1', 'enslaved')
4552 self
.wait_operstate('bond99', 'routable')
4554 check_output('ip link set dummy98 up')
4556 self
.wait_operstate('dummy98', 'enslaved')
4557 self
.wait_operstate('test1', 'enslaved')
4558 self
.wait_operstate('bond99', 'routable')
4560 check_output('ip link set dummy98 down')
4561 check_output('ip link set test1 down')
4563 self
.wait_operstate('dummy98', 'off')
4564 self
.wait_operstate('test1', 'off')
4566 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4567 # Huh? Kernel does not recognize that all slave interfaces are down?
4568 # Let's confirm that networkd's operstate is consistent with ip's result.
4569 self
.assertNotRegex(output
, 'NO-CARRIER')
4571 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4579 def test_bridge_mac_none(self
):
4580 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4581 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4583 self
.wait_online(['dummy98:enslaved', 'bridge99:degraded'])
4585 output
= check_output('ip link show dev dummy98')
4587 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4589 output
= check_output('ip link show dev bridge99')
4591 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4593 def test_bridge_vlan(self
):
4594 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4595 '26-bridge.netdev', '26-bridge-vlan-master.network',
4598 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4600 output
= check_output('bridge vlan show dev test1')
4602 # check if the default VID is removed
4603 self
.assertNotIn('1 Egress Untagged', output
)
4604 for i
in range(1000, 3000):
4606 self
.assertIn(f
'{i} PVID', output
)
4607 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4608 self
.assertIn(f
'{i} Egress Untagged', output
)
4609 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4610 self
.assertIn(f
'{i}', output
)
4612 self
.assertNotIn(f
'{i}', output
)
4614 output
= check_output('bridge vlan show dev bridge99')
4616 # check if the default VID is removed
4617 self
.assertNotIn('1 Egress Untagged', output
)
4618 for i
in range(1000, 3000):
4620 self
.assertIn(f
'{i} PVID', output
)
4621 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4622 self
.assertIn(f
'{i} Egress Untagged', output
)
4623 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4624 self
.assertIn(f
'{i}', output
)
4626 self
.assertNotIn(f
'{i}', output
)
4629 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4630 '26-bridge-vlan-master.network.d/10-override.conf')
4632 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4634 output
= check_output('bridge vlan show dev test1')
4636 for i
in range(1000, 3000):
4638 self
.assertIn(f
'{i} PVID', output
)
4639 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4640 self
.assertIn(f
'{i} Egress Untagged', output
)
4641 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4642 self
.assertIn(f
'{i}', output
)
4644 self
.assertNotIn(f
'{i}', output
)
4646 output
= check_output('bridge vlan show dev bridge99')
4648 for i
in range(1000, 3000):
4650 self
.assertIn(f
'{i} PVID', output
)
4651 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4652 self
.assertIn(f
'{i} Egress Untagged', output
)
4653 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4654 self
.assertIn(f
'{i}', output
)
4656 self
.assertNotIn(f
'{i}', output
)
4658 # Remove several vlan IDs
4659 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4660 '26-bridge-vlan-master.network.d/20-override.conf')
4662 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4664 output
= check_output('bridge vlan show dev test1')
4666 for i
in range(1000, 3000):
4668 self
.assertIn(f
'{i} PVID', output
)
4669 elif i
in range(2012, 2016):
4670 self
.assertIn(f
'{i} Egress Untagged', output
)
4671 elif i
in range(2008, 2014):
4672 self
.assertIn(f
'{i}', output
)
4674 self
.assertNotIn(f
'{i}', output
)
4676 output
= check_output('bridge vlan show dev bridge99')
4678 for i
in range(1000, 3000):
4680 self
.assertIn(f
'{i} PVID', output
)
4681 elif i
in range(2022, 2026):
4682 self
.assertIn(f
'{i} Egress Untagged', output
)
4683 elif i
in range(2018, 2024):
4684 self
.assertIn(f
'{i}', output
)
4686 self
.assertNotIn(f
'{i}', output
)
4688 # Remove all vlan IDs
4689 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4690 '26-bridge-vlan-master.network.d/30-override.conf')
4692 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4694 output
= check_output('bridge vlan show dev test1')
4696 self
.assertNotIn('PVID', output
)
4697 for i
in range(1000, 3000):
4698 self
.assertNotIn(f
'{i}', output
)
4700 output
= check_output('bridge vlan show dev bridge99')
4702 self
.assertNotIn('PVID', output
)
4703 for i
in range(1000, 3000):
4704 self
.assertNotIn(f
'{i}', output
)
4706 def test_bridge_vlan_issue_20373(self
):
4707 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4708 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4709 '21-vlan.netdev', '21-vlan.network')
4711 self
.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
4713 output
= check_output('bridge vlan show dev test1')
4715 self
.assertIn('100 PVID Egress Untagged', output
)
4716 self
.assertIn('560', output
)
4717 self
.assertIn('600', output
)
4719 output
= check_output('bridge vlan show dev bridge99')
4721 self
.assertIn('1 PVID Egress Untagged', output
)
4722 self
.assertIn('100', output
)
4723 self
.assertIn('600', output
)
4725 def test_bridge_mdb(self
):
4726 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4727 '26-bridge.netdev', '26-bridge-mdb-master.network')
4729 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
4731 output
= check_output('bridge mdb show dev bridge99')
4733 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4734 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4736 # Old kernel may not support bridge MDB entries on bridge master
4737 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4738 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4739 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4741 def test_bridge_keep_master(self
):
4742 check_output('ip link add bridge99 type bridge')
4743 check_output('ip link set bridge99 up')
4744 check_output('ip link add dummy98 type dummy')
4745 check_output('ip link set dummy98 master bridge99')
4747 copy_network_unit('23-keep-master.network')
4749 self
.wait_online(['dummy98:enslaved'])
4751 output
= check_output('ip -d link show dummy98')
4753 self
.assertRegex(output
, 'master bridge99')
4754 self
.assertRegex(output
, 'bridge')
4756 output
= check_output('bridge -d link show dummy98')
4758 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4759 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4760 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4761 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4762 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4763 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4764 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4765 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4766 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4767 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4768 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4769 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4771 def test_bridge_property(self
):
4772 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4773 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4774 '25-bridge99.network')
4776 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4778 output
= check_output('ip -d link show bridge99')
4780 self
.assertIn('mtu 9000 ', output
)
4782 output
= check_output('ip -d link show test1')
4784 self
.assertIn('master bridge99 ', output
)
4785 self
.assertIn('bridge_slave', output
)
4786 self
.assertIn('mtu 9000 ', output
)
4788 output
= check_output('ip -d link show dummy98')
4790 self
.assertIn('master bridge99 ', output
)
4791 self
.assertIn('bridge_slave', output
)
4792 self
.assertIn('mtu 9000 ', output
)
4794 output
= check_output('ip addr show bridge99')
4796 self
.assertIn('192.168.0.15/24', output
)
4798 output
= check_output('bridge -d link show dummy98')
4800 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4801 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4802 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4803 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4804 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4805 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4806 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4807 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4808 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4809 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4810 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4811 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4812 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4814 output
= check_output('bridge -d link show test1')
4816 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4818 check_output('ip address add 192.168.0.16/24 dev bridge99')
4819 output
= check_output('ip addr show bridge99')
4821 self
.assertIn('192.168.0.16/24', output
)
4824 print('### ip -6 route list table all dev bridge99')
4825 output
= check_output('ip -6 route list table all dev bridge99')
4827 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4829 remove_link('test1')
4830 self
.wait_operstate('bridge99', 'routable')
4832 output
= check_output('ip -d link show bridge99')
4834 self
.assertIn('mtu 9000 ', output
)
4836 output
= check_output('ip -d link show dummy98')
4838 self
.assertIn('master bridge99 ', output
)
4839 self
.assertIn('bridge_slave', output
)
4840 self
.assertIn('mtu 9000 ', output
)
4842 remove_link('dummy98')
4843 self
.wait_operstate('bridge99', 'no-carrier')
4845 output
= check_output('ip -d link show bridge99')
4847 # When no carrier, the kernel may reset the MTU
4848 self
.assertIn('NO-CARRIER', output
)
4850 output
= check_output('ip address show bridge99')
4852 self
.assertNotIn('192.168.0.15/24', output
)
4853 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4855 print('### ip -6 route list table all dev bridge99')
4856 output
= check_output('ip -6 route list table all dev bridge99')
4858 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4860 check_output('ip link add dummy98 type dummy')
4861 self
.wait_online(['dummy98:enslaved', 'bridge99:routable'])
4863 output
= check_output('ip -d link show bridge99')
4865 self
.assertIn('mtu 9000 ', output
)
4867 output
= check_output('ip -d link show dummy98')
4869 self
.assertIn('master bridge99 ', output
)
4870 self
.assertIn('bridge_slave', output
)
4871 self
.assertIn('mtu 9000 ', output
)
4873 def test_bridge_configure_without_carrier(self
):
4874 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4878 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4879 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4880 with self
.subTest(test
=test
):
4881 if test
== 'no-slave':
4882 # bridge has no slaves; it's up but *might* not have carrier
4883 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4884 # due to a bug in the kernel, newly-created bridges are brought up
4885 # *with* carrier, unless they have had any setting changed; e.g.
4886 # their mac set, priority set, etc. Then, they will lose carrier
4887 # as soon as a (down) slave interface is added, and regain carrier
4888 # again once the slave interface is brought up.
4889 #self.check_link_attr('bridge99', 'carrier', '0')
4890 elif test
== 'add-slave':
4891 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4892 self
.check_link_attr('test1', 'operstate', 'down')
4893 check_output('ip link set dev test1 master bridge99')
4894 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4895 self
.check_link_attr('bridge99', 'carrier', '0')
4896 elif test
== 'slave-up':
4897 # bring up slave, which will have carrier; bridge gains carrier
4898 check_output('ip link set dev test1 up')
4899 self
.wait_online(['bridge99:routable'])
4900 self
.check_link_attr('bridge99', 'carrier', '1')
4901 elif test
== 'slave-no-carrier':
4902 # drop slave carrier; bridge loses carrier
4903 check_output('ip link set dev test1 carrier off')
4904 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4905 self
.check_link_attr('bridge99', 'carrier', '0')
4906 elif test
== 'slave-carrier':
4907 # restore slave carrier; bridge gains carrier
4908 check_output('ip link set dev test1 carrier on')
4909 self
.wait_online(['bridge99:routable'])
4910 self
.check_link_attr('bridge99', 'carrier', '1')
4911 elif test
== 'slave-down':
4912 # bring down slave; bridge loses carrier
4913 check_output('ip link set dev test1 down')
4914 self
.wait_online(['bridge99:no-carrier:no-carrier'])
4915 self
.check_link_attr('bridge99', 'carrier', '0')
4917 output
= networkctl_status('bridge99')
4918 self
.assertRegex(output
, '10.1.2.3')
4919 self
.assertRegex(output
, '10.1.2.1')
4921 def test_bridge_ignore_carrier_loss(self
):
4922 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4923 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4924 '25-bridge99-ignore-carrier-loss.network')
4926 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4928 check_output('ip address add 192.168.0.16/24 dev bridge99')
4929 remove_link('test1', 'dummy98')
4932 output
= check_output('ip address show bridge99')
4934 self
.assertRegex(output
, 'NO-CARRIER')
4935 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4936 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
4938 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
4939 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4940 '25-bridge99-ignore-carrier-loss.network')
4942 self
.wait_online(['bridge99:no-carrier'])
4944 for trial
in range(4):
4945 check_output('ip link add dummy98 type dummy')
4946 check_output('ip link set dummy98 up')
4948 remove_link('dummy98')
4950 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4952 output
= check_output('ip address show bridge99')
4954 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4956 output
= check_output('ip rule list table 100')
4958 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
4960 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
4968 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4969 def test_sriov(self
):
4970 copy_network_unit('25-default.link', '25-sriov.network')
4972 call('modprobe netdevsim')
4974 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
4977 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
4981 self
.wait_online(['eni99np1:routable'])
4983 output
= check_output('ip link show dev eni99np1')
4985 self
.assertRegex(output
,
4986 '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 *'
4987 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4988 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4991 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4992 def test_sriov_udev(self
):
4993 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
4995 call('modprobe netdevsim')
4997 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5001 self
.wait_online(['eni99np1:routable'])
5003 # the name eni99np1 may be an alternative name.
5004 ifname
= link_resolve('eni99np1')
5006 output
= check_output('ip link show dev eni99np1')
5008 self
.assertRegex(output
,
5009 '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 *'
5010 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5011 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5013 self
.assertNotIn('vf 3', output
)
5014 self
.assertNotIn('vf 4', output
)
5016 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5017 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5020 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5022 output
= check_output('ip link show dev eni99np1')
5024 self
.assertRegex(output
,
5025 '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 *'
5026 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5027 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5030 self
.assertNotIn('vf 4', output
)
5032 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5033 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5036 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5038 output
= check_output('ip link show dev eni99np1')
5040 self
.assertRegex(output
,
5041 '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 *'
5042 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5043 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5046 self
.assertNotIn('vf 4', output
)
5048 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5049 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5052 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5054 output
= check_output('ip link show dev eni99np1')
5056 self
.assertRegex(output
,
5057 '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 *'
5058 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5060 self
.assertNotIn('vf 2', output
)
5061 self
.assertNotIn('vf 3', output
)
5062 self
.assertNotIn('vf 4', output
)
5064 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5065 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5068 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5070 output
= check_output('ip link show dev eni99np1')
5072 self
.assertRegex(output
,
5073 '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 *'
5074 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5075 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5077 self
.assertNotIn('vf 3', output
)
5078 self
.assertNotIn('vf 4', output
)
5080 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5088 def test_lldp(self
):
5089 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5091 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
5093 for trial
in range(10):
5097 output
= networkctl('lldp')
5099 if re
.search(r
'veth99 .* veth-peer', output
):
5104 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5112 def test_ipv6_prefix_delegation(self
):
5113 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5114 self
.setup_nftset('addr6', 'ipv6_addr')
5115 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5116 self
.setup_nftset('ifindex', 'iface_index')
5118 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5120 output
= resolvectl('dns', 'veth99')
5122 self
.assertRegex(output
, 'fe80::')
5123 self
.assertRegex(output
, '2002:da8:1::1')
5125 output
= resolvectl('domain', 'veth99')
5127 self
.assertIn('hogehoge.test', output
)
5129 output
= networkctl_status('veth99')
5131 self
.assertRegex(output
, '2002:da8:1:0')
5133 self
.check_netlabel('veth99', '2002:da8:1::/64')
5134 self
.check_netlabel('veth99', '2002:da8:2::/64')
5136 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5137 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5138 self
.check_nftset('network6', '2002:da8:1::/64')
5139 self
.check_nftset('network6', '2002:da8:2::/64')
5140 self
.check_nftset('ifindex', 'veth99')
5142 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5144 def check_ipv6_token_static(self
):
5145 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5147 output
= networkctl_status('veth99')
5149 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5150 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5151 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5152 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5154 def test_ipv6_token_static(self
):
5155 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5158 self
.check_ipv6_token_static()
5161 check_output('ip link set veth99 down')
5162 check_output('ip link set veth99 up')
5164 self
.check_ipv6_token_static()
5167 check_output('ip link set veth99 down')
5168 time
.sleep(random
.uniform(0, 0.1))
5169 check_output('ip link set veth99 up')
5170 time
.sleep(random
.uniform(0, 0.1))
5172 self
.check_ipv6_token_static()
5174 def test_ipv6_token_prefixstable(self
):
5175 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5177 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5179 output
= networkctl_status('veth99')
5181 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5182 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5184 def test_ipv6_token_prefixstable_without_address(self
):
5185 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5187 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
5189 output
= networkctl_status('veth99')
5191 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5192 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5194 def test_router_preference(self
):
5195 copy_network_unit('25-veth-client.netdev',
5196 '25-veth-router-high.netdev',
5197 '25-veth-router-low.netdev',
5199 '25-veth-bridge.network',
5200 '25-veth-client.network',
5201 '25-veth-router-high.network',
5202 '25-veth-router-low.network',
5203 '25-bridge99.network')
5205 self
.wait_online(['client-p:enslaved',
5206 'router-high:degraded', 'router-high-p:enslaved',
5207 'router-low:degraded', 'router-low-p:enslaved',
5208 'bridge99:routable'])
5210 networkctl_reconfigure('client')
5211 self
.wait_online(['client:routable'])
5213 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5214 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5215 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5216 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5218 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5220 self
.assertIn('pref high', output
)
5221 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5223 self
.assertIn('pref low', output
)
5225 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5226 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5229 self
.wait_online(['client:routable'])
5231 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5232 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5233 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5234 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5236 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5238 self
.assertIn('pref high', output
)
5239 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5241 self
.assertIn('pref low', output
)
5243 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5244 def test_captive_portal(self
):
5245 copy_network_unit('25-veth-client.netdev',
5246 '25-veth-router-captive.netdev',
5248 '25-veth-client-captive.network',
5249 '25-veth-router-captive.network',
5250 '25-veth-bridge-captive.network',
5251 '25-bridge99.network')
5253 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5254 'router-captive:degraded', 'router-captivep:enslaved'])
5256 start_radvd(config_file
='captive-portal.conf')
5257 networkctl_reconfigure('client')
5258 self
.wait_online(['client:routable'])
5260 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5261 output
= networkctl_status('client')
5263 self
.assertIn('Captive Portal: http://systemd.io', output
)
5265 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5266 def test_invalid_captive_portal(self
):
5267 def radvd_write_config(captive_portal_uri
):
5268 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5269 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5271 captive_portal_uris
= [
5272 "42ěščěškd ěšč ě s",
5277 copy_network_unit('25-veth-client.netdev',
5278 '25-veth-router-captive.netdev',
5280 '25-veth-client-captive.network',
5281 '25-veth-router-captive.network',
5282 '25-veth-bridge-captive.network',
5283 '25-bridge99.network')
5285 self
.wait_online(['bridge99:routable', 'client-p:enslaved',
5286 'router-captive:degraded', 'router-captivep:enslaved'])
5288 for uri
in captive_portal_uris
:
5289 print(f
"Captive portal: {uri}")
5290 radvd_write_config(uri
)
5292 start_radvd(config_file
='bogus-captive-portal.conf')
5293 networkctl_reconfigure('client')
5294 self
.wait_online(['client:routable'])
5296 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5297 output
= networkctl_status('client')
5299 self
.assertNotIn('Captive Portal:', output
)
5301 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5309 def test_dhcp_server(self
):
5310 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5312 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5314 output
= networkctl_status('veth99')
5316 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5317 self
.assertIn('Gateway: 192.168.5.3', output
)
5318 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5319 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5321 output
= networkctl_status('veth-peer')
5322 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5324 def test_dhcp_server_null_server_address(self
):
5325 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5327 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5329 output
= check_output('ip --json address show dev veth-peer')
5330 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5331 print(server_address
)
5333 output
= check_output('ip --json address show dev veth99')
5334 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5335 print(client_address
)
5337 output
= networkctl_status('veth99')
5339 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5340 self
.assertIn(f
'Gateway: {server_address}', output
)
5341 self
.assertIn(f
'DNS: {server_address}', output
)
5342 self
.assertIn(f
'NTP: {server_address}', output
)
5344 output
= networkctl_status('veth-peer')
5345 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5347 def test_dhcp_server_with_uplink(self
):
5348 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5349 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5351 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5353 output
= networkctl_status('veth99')
5355 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5356 self
.assertIn('Gateway: 192.168.5.3', output
)
5357 self
.assertIn('DNS: 192.168.5.1', output
)
5358 self
.assertIn('NTP: 192.168.5.1', output
)
5360 def test_emit_router_timezone(self
):
5361 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5363 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5365 output
= networkctl_status('veth99')
5367 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5368 self
.assertIn('Gateway: 192.168.5.1', output
)
5369 self
.assertIn('Time Zone: Europe/Berlin', output
)
5371 def test_dhcp_server_static_lease(self
):
5372 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5374 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5376 output
= networkctl_status('veth99')
5378 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5379 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5381 def test_dhcp_server_static_lease_default_client_id(self
):
5382 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5384 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5386 output
= networkctl_status('veth99')
5388 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5389 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5391 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5399 def test_relay_agent(self
):
5400 copy_network_unit('25-agent-veth-client.netdev',
5401 '25-agent-veth-server.netdev',
5402 '25-agent-client.network',
5403 '25-agent-server.network',
5404 '25-agent-client-peer.network',
5405 '25-agent-server-peer.network')
5408 self
.wait_online(['client:routable'])
5410 output
= networkctl_status('client')
5412 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5414 def test_replay_agent_on_bridge(self
):
5415 copy_network_unit('25-agent-bridge.netdev',
5416 '25-agent-veth-client.netdev',
5417 '25-agent-bridge.network',
5418 '25-agent-bridge-port.network',
5419 '25-agent-client.network')
5421 self
.wait_online(['bridge-relay:routable', 'client-peer:enslaved'])
5424 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5426 if expect
in read_networkd_log():
5432 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5440 def test_dhcp_client_ipv6_only(self
):
5441 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5444 self
.wait_online(['veth-peer:carrier'])
5446 # information request mode
5447 # The name ipv6-only option may not be supported by older dnsmasq
5448 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5449 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5450 '--dhcp-option=option6:dns-server,[2600::ee]',
5451 '--dhcp-option=option6:ntp-server,[2600::ff]',
5452 ra_mode
='ra-stateless')
5453 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5455 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5456 # Let's wait for the expected DNS server being listed in the state file.
5457 for _
in range(100):
5458 output
= read_link_state_file('veth99')
5459 if 'DNS=2600::ee' in output
:
5463 # Check link state file
5464 print('## link state file')
5465 output
= read_link_state_file('veth99')
5467 self
.assertIn('DNS=2600::ee', output
)
5468 self
.assertIn('NTP=2600::ff', output
)
5470 # Check manager state file
5471 print('## manager state file')
5472 output
= read_manager_state_file()
5474 self
.assertRegex(output
, 'DNS=.*2600::ee')
5475 self
.assertRegex(output
, 'NTP=.*2600::ff')
5477 print('## dnsmasq log')
5478 output
= read_dnsmasq_log_file()
5480 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5481 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5482 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5483 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5484 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5487 check_json(networkctl_json('veth99'))
5491 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5492 '--dhcp-option=option6:dns-server,[2600::ee]',
5493 '--dhcp-option=option6:ntp-server,[2600::ff]')
5494 networkctl_reconfigure('veth99')
5495 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5498 output
= check_output('ip address show dev veth99 scope global')
5500 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5501 self
.assertNotIn('192.168.5', output
)
5503 # checking semi-static route
5504 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5506 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5508 # Confirm that ipv6 token is not set in the kernel
5509 output
= check_output('ip token show dev veth99')
5511 self
.assertRegex(output
, 'token :: dev veth99')
5513 # Make manager and link state file updated
5514 resolvectl('revert', 'veth99')
5516 # Check link state file
5517 print('## link state file')
5518 output
= read_link_state_file('veth99')
5520 self
.assertIn('DNS=2600::ee', output
)
5521 self
.assertIn('NTP=2600::ff', output
)
5523 # Check manager state file
5524 print('## manager state file')
5525 output
= read_manager_state_file()
5527 self
.assertRegex(output
, 'DNS=.*2600::ee')
5528 self
.assertRegex(output
, 'NTP=.*2600::ff')
5530 print('## dnsmasq log')
5531 output
= read_dnsmasq_log_file()
5533 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5534 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5535 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5536 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5537 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5538 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5541 check_json(networkctl_json('veth99'))
5543 # Testing without rapid commit support
5544 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5545 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5548 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5549 '--dhcp-option=option6:dns-server,[2600::ee]',
5550 '--dhcp-option=option6:ntp-server,[2600::ff]')
5553 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5556 output
= check_output('ip address show dev veth99 scope global')
5558 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5559 self
.assertNotIn('192.168.5', output
)
5561 # checking semi-static route
5562 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5564 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5566 # Make manager and link state file updated
5567 resolvectl('revert', 'veth99')
5569 # Check link state file
5570 print('## link state file')
5571 output
= read_link_state_file('veth99')
5573 self
.assertIn('DNS=2600::ee', output
)
5574 self
.assertIn('NTP=2600::ff', output
)
5576 # Check manager state file
5577 print('## manager state file')
5578 output
= read_manager_state_file()
5580 self
.assertRegex(output
, 'DNS=.*2600::ee')
5581 self
.assertRegex(output
, 'NTP=.*2600::ff')
5583 print('## dnsmasq log')
5584 output
= read_dnsmasq_log_file()
5586 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5587 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5588 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5589 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5590 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5591 self
.assertNotIn('rapid-commit', output
)
5594 check_json(networkctl_json('veth99'))
5596 def test_dhcp_client_ipv6_dbus_status(self
):
5597 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5599 self
.wait_online(['veth-peer:carrier'])
5601 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5602 # bit set) has yet been received and the configuration does not include WithoutRA=true
5603 state
= get_dhcp6_client_state('veth99')
5604 print(f
"DHCPv6 client state = {state}")
5605 self
.assertEqual(state
, 'stopped')
5607 state
= get_dhcp4_client_state('veth99')
5608 print(f
"DHCPv4 client state = {state}")
5609 self
.assertEqual(state
, 'selecting')
5611 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5612 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5614 state
= get_dhcp6_client_state('veth99')
5615 print(f
"DHCPv6 client state = {state}")
5616 self
.assertEqual(state
, 'bound')
5618 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5619 for _
in range(100):
5620 state
= get_dhcp4_client_state('veth99')
5621 if state
== 'stopped':
5625 print(f
"DHCPv4 client state = {state}")
5626 self
.assertEqual(state
, 'stopped')
5628 # restart dnsmasq to clear log
5630 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5632 # Test renew command
5633 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5634 networkctl('renew', 'veth99')
5636 for _
in range(100):
5637 state
= get_dhcp4_client_state('veth99')
5638 if state
== 'stopped':
5642 print(f
"DHCPv4 client state = {state}")
5643 self
.assertEqual(state
, 'stopped')
5645 print('## dnsmasq log')
5646 output
= read_dnsmasq_log_file()
5648 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5649 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5650 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5651 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5653 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5654 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5657 self
.wait_online(['veth-peer:carrier'])
5659 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5662 output
= check_output('ip address show dev veth99 scope global')
5664 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5665 self
.assertNotIn('192.168.5', output
)
5667 print('## dnsmasq log')
5668 output
= read_dnsmasq_log_file()
5670 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5671 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5672 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5673 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5674 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5676 def test_dhcp_client_ipv4_only(self
):
5677 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5679 self
.setup_nftset('addr4', 'ipv4_addr')
5680 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5681 self
.setup_nftset('ifindex', 'iface_index')
5684 self
.wait_online(['veth-peer:carrier'])
5685 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5686 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5687 '--dhcp-option=option:domain-search,example.com',
5688 '--dhcp-alternate-port=67,5555',
5689 ipv4_range
='192.168.5.110,192.168.5.119')
5690 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5691 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5693 print('## ip address show dev veth99 scope global')
5694 output
= check_output('ip address show dev veth99 scope global')
5696 self
.assertIn('mtu 1492', output
)
5697 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5698 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')
5699 self
.assertNotIn('2600::', output
)
5701 output
= check_output('ip -4 --json address show dev veth99')
5702 for i
in json
.loads(output
)[0]['addr_info']:
5703 if i
['label'] == 'test-label':
5704 address1
= i
['local']
5707 self
.assertFalse(True)
5709 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5711 print('## ip route show table main dev veth99')
5712 output
= check_output('ip route show table main dev veth99')
5714 # no DHCP routes assigned to the main table
5715 self
.assertNotIn('proto dhcp', output
)
5717 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5718 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5719 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5720 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5722 print('## ip route show table 211 dev veth99')
5723 output
= check_output('ip route show table 211 dev veth99')
5725 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5726 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5727 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5728 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5729 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5730 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5732 print('## link state file')
5733 output
= read_link_state_file('veth99')
5735 # checking DNS server, SIP server, and Domains
5736 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5737 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5738 self
.assertIn('DOMAINS=example.com', output
)
5741 j
= json
.loads(networkctl_json('veth99'))
5743 self
.assertEqual(len(j
['DNS']), 2)
5746 self
.assertEqual(i
['Family'], 2)
5747 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5748 self
.assertRegex(a
, '^192.168.5.[67]$')
5749 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5750 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5751 self
.assertEqual('192.168.5.1', a
)
5753 self
.assertEqual(len(j
['SIP']), 2)
5756 self
.assertEqual(i
['Family'], 2)
5757 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5758 self
.assertRegex(a
, '^192.168.5.2[12]$')
5759 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5760 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5761 self
.assertEqual('192.168.5.1', a
)
5763 print('## dnsmasq log')
5764 output
= read_dnsmasq_log_file()
5766 self
.assertIn('vendor class: FooBarVendorTest', output
)
5767 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5768 self
.assertIn('client provides name: test-hostname', output
)
5769 self
.assertIn('26:mtu', output
)
5771 # change address range, DNS servers, and Domains
5773 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5774 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5775 '--dhcp-option=option:domain-search,foo.example.com',
5776 '--dhcp-alternate-port=67,5555',
5777 ipv4_range
='192.168.5.120,192.168.5.129',)
5779 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5780 print('Wait for the DHCP lease to be expired')
5781 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5782 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5784 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5786 print('## ip address show dev veth99 scope global')
5787 output
= check_output('ip address show dev veth99 scope global')
5789 self
.assertIn('mtu 1492', output
)
5790 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5791 self
.assertNotIn(f
'{address1}', output
)
5792 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')
5793 self
.assertNotIn('2600::', output
)
5795 output
= check_output('ip -4 --json address show dev veth99')
5796 for i
in json
.loads(output
)[0]['addr_info']:
5797 if i
['label'] == 'test-label':
5798 address2
= i
['local']
5801 self
.assertFalse(True)
5803 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5805 print('## ip route show table main dev veth99')
5806 output
= check_output('ip route show table main dev veth99')
5808 # no DHCP routes assigned to the main table
5809 self
.assertNotIn('proto dhcp', output
)
5811 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5812 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5813 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5814 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5816 print('## ip route show table 211 dev veth99')
5817 output
= check_output('ip route show table 211 dev veth99')
5819 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5820 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5821 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5822 self
.assertNotIn('192.168.5.6', output
)
5823 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5824 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5825 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5827 print('## link state file')
5828 output
= read_link_state_file('veth99')
5830 # checking DNS server, SIP server, and Domains
5831 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5832 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5833 self
.assertIn('DOMAINS=foo.example.com', output
)
5836 j
= json
.loads(networkctl_json('veth99'))
5838 self
.assertEqual(len(j
['DNS']), 3)
5841 self
.assertEqual(i
['Family'], 2)
5842 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5843 self
.assertRegex(a
, '^192.168.5.[178]$')
5844 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5845 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5846 self
.assertEqual('192.168.5.1', a
)
5848 self
.assertEqual(len(j
['SIP']), 2)
5851 self
.assertEqual(i
['Family'], 2)
5852 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5853 self
.assertRegex(a
, '^192.168.5.2[34]$')
5854 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5855 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5856 self
.assertEqual('192.168.5.1', a
)
5858 print('## dnsmasq log')
5859 output
= read_dnsmasq_log_file()
5861 self
.assertIn('vendor class: FooBarVendorTest', output
)
5862 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
5863 self
.assertIn('client provides name: test-hostname', output
)
5864 self
.assertIn('26:mtu', output
)
5866 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
5868 self
.check_nftset('addr4', r
'192\.168\.5\.1')
5869 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
5870 self
.check_nftset('ifindex', 'veth99')
5872 self
.teardown_nftset('addr4', 'network4', 'ifindex')
5874 def test_dhcp_client_ipv4_dbus_status(self
):
5875 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5877 self
.wait_online(['veth-peer:carrier'])
5879 state
= get_dhcp4_client_state('veth99')
5880 print(f
"State = {state}")
5881 self
.assertEqual(state
, 'rebooting')
5883 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5884 '--dhcp-option=option:domain-search,example.com',
5885 '--dhcp-alternate-port=67,5555',
5886 ipv4_range
='192.168.5.110,192.168.5.119')
5887 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5888 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5890 state
= get_dhcp4_client_state('veth99')
5891 print(f
"State = {state}")
5892 self
.assertEqual(state
, 'bound')
5894 def test_dhcp_client_allow_list(self
):
5895 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
5898 self
.wait_online(['veth-peer:carrier'])
5899 since
= datetime
.datetime
.now()
5902 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5904 if expect
in read_networkd_log(since
=since
):
5910 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
5911 since
= datetime
.datetime
.now()
5914 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
5916 if expect
in read_networkd_log(since
=since
):
5922 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
5923 since
= datetime
.datetime
.now()
5926 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
5928 if expect
in read_networkd_log(since
=since
):
5934 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
5935 def test_dhcp_client_rapid_commit(self
):
5936 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
5938 self
.wait_online(['veth-peer:carrier'])
5940 start_dnsmasq('--dhcp-rapid-commit')
5941 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
5942 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5944 state
= get_dhcp4_client_state('veth99')
5945 print(f
"DHCPv4 client state = {state}")
5946 self
.assertEqual(state
, 'bound')
5948 output
= read_dnsmasq_log_file()
5949 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
5950 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
5951 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5952 self
.assertIn('DHCPACK(veth-peer)', output
)
5954 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
5955 copy_network_unit('25-veth.netdev',
5956 '25-dhcp-server-ipv6-only-mode.network',
5957 '25-dhcp-client-ipv6-only-mode.network')
5959 self
.wait_online(['veth99:routable', 'veth-peer:routable'], timeout
='40s')
5960 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
5962 state
= get_dhcp4_client_state('veth99')
5963 print(f
"State = {state}")
5964 self
.assertEqual(state
, 'bound')
5966 def test_dhcp_client_ipv4_use_routes_gateway(self
):
5968 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
5974 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
5975 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
5976 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
5978 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
5979 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
5980 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
5981 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
5982 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
5983 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
5984 copy_network_unit(*testunits
, copy_dropins
=False)
5987 self
.wait_online(['veth-peer:carrier'])
5988 additional_options
= [
5989 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
5990 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
5991 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
5994 additional_options
+= [
5995 '--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'
5997 start_dnsmasq(*additional_options
)
5998 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6000 output
= check_output('ip -4 route show dev veth99')
6006 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6007 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6008 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6009 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6010 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6012 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6013 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6014 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6015 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6017 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6018 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6019 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6020 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6021 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6022 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6023 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6024 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6027 if use_gateway
and (not classless
or not use_routes
):
6028 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6030 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6032 # Check route to gateway
6033 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6034 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6036 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6038 # Check RoutesToDNS= and RoutesToNTP=
6039 if dns_and_ntp_routes
:
6040 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6041 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6044 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6045 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6047 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6048 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6050 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6051 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6053 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6054 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6055 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6056 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6058 check_json(networkctl_json())
6060 def test_dhcp_client_settings_anonymize(self
):
6061 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6063 self
.wait_online(['veth-peer:carrier'])
6065 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6067 print('## dnsmasq log')
6068 output
= read_dnsmasq_log_file()
6070 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6071 self
.assertNotIn('test-hostname', output
)
6072 self
.assertNotIn('26:mtu', output
)
6074 def test_dhcp_keep_configuration_dhcp(self
):
6075 copy_network_unit('25-veth.netdev',
6076 '25-dhcp-server-veth-peer.network',
6077 '25-dhcp-client-keep-configuration-dhcp.network')
6079 self
.wait_online(['veth-peer:carrier'])
6081 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6083 output
= check_output('ip address show dev veth99 scope global')
6085 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6086 'valid_lft forever preferred_lft forever')
6088 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6091 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6092 print('Wait for the DHCP lease to be expired')
6095 # The lease address should be kept after the lease expired
6096 output
= check_output('ip address show dev veth99 scope global')
6098 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6099 'valid_lft forever preferred_lft forever')
6103 # The lease address should be kept after networkd stopped
6104 output
= check_output('ip address show dev veth99 scope global')
6106 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6107 'valid_lft forever preferred_lft forever')
6109 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6110 f
.write('[Network]\nDHCP=no\n')
6113 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6115 # Still the lease address should be kept after networkd restarted
6116 output
= check_output('ip address show dev veth99 scope global')
6118 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6119 'valid_lft forever preferred_lft forever')
6121 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6122 copy_network_unit('25-veth.netdev',
6123 '25-dhcp-server-veth-peer.network',
6124 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6126 self
.wait_online(['veth-peer:carrier'])
6128 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6130 output
= check_output('ip address show dev veth99 scope global')
6132 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6137 output
= check_output('ip address show dev veth99 scope global')
6139 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6142 self
.wait_online(['veth-peer:routable'])
6144 output
= check_output('ip address show dev veth99 scope global')
6146 self
.assertNotIn('192.168.5.', output
)
6148 def test_dhcp_client_reuse_address_as_static(self
):
6149 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6151 self
.wait_online(['veth-peer:carrier'])
6153 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6155 # link become 'routable' when at least one protocol provide an valid address.
6156 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6157 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6159 output
= check_output('ip address show dev veth99 scope global')
6160 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6161 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6162 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6163 print(static_network
)
6165 remove_network_unit('25-dhcp-client.network')
6167 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6168 f
.write(static_network
)
6171 self
.wait_online(['veth99:routable'])
6173 output
= check_output('ip -4 address show dev veth99 scope global')
6175 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6176 'valid_lft forever preferred_lft forever')
6178 output
= check_output('ip -6 address show dev veth99 scope global')
6180 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6181 'valid_lft forever preferred_lft forever')
6183 @expectedFailureIfModuleIsNotAvailable('vrf')
6184 def test_dhcp_client_vrf(self
):
6185 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6186 '25-vrf.netdev', '25-vrf.network')
6188 self
.wait_online(['veth-peer:carrier'])
6190 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
6192 # link become 'routable' when at least one protocol provide an valid address.
6193 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6194 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6196 print('## ip -d link show dev vrf99')
6197 output
= check_output('ip -d link show dev vrf99')
6199 self
.assertRegex(output
, 'vrf table 42')
6201 print('## ip address show vrf vrf99')
6202 output
= check_output('ip address show vrf vrf99')
6204 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6205 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6206 self
.assertRegex(output
, 'inet6 .* scope link')
6208 print('## ip address show dev veth99')
6209 output
= check_output('ip address show dev veth99')
6211 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6212 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6213 self
.assertRegex(output
, 'inet6 .* scope link')
6215 print('## ip route show vrf vrf99')
6216 output
= check_output('ip route show vrf vrf99')
6218 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6219 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6220 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6222 print('## ip route show table main dev veth99')
6223 output
= check_output('ip route show table main dev veth99')
6225 self
.assertEqual(output
, '')
6227 def test_dhcp_client_gateway_onlink_implicit(self
):
6228 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6229 '25-dhcp-client-gateway-onlink-implicit.network')
6231 self
.wait_online(['veth-peer:carrier'])
6233 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
6235 output
= networkctl_status('veth99')
6237 self
.assertRegex(output
, '192.168.5')
6239 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6241 self
.assertRegex(output
, 'onlink')
6242 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6244 self
.assertRegex(output
, 'onlink')
6246 def test_dhcp_client_with_ipv4ll(self
):
6247 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6248 '25-dhcp-client-with-ipv4ll.network')
6250 # we need to increase timeout above default, as this will need to wait for
6251 # systemd-networkd to get the dhcpv4 transient failure event
6252 self
.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout
='60s')
6254 output
= check_output('ip -4 address show dev veth99')
6256 self
.assertNotIn('192.168.5.', output
)
6257 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6260 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6261 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6262 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')
6263 self
.wait_online(['veth99:routable'])
6265 output
= check_output('ip -4 address show dev veth99')
6267 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6268 self
.assertNotIn('169.254.', output
)
6269 self
.assertNotIn('scope link', output
)
6272 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6273 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)
6274 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6276 output
= check_output('ip -4 address show dev veth99')
6278 self
.assertNotIn('192.168.5.', output
)
6279 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6281 def test_dhcp_client_use_dns(self
):
6282 def check(self
, ipv4
, ipv6
):
6283 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6284 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6285 f
.write('[DHCPv4]\nUseDNS=')
6286 f
.write('yes' if ipv4
else 'no')
6287 f
.write('\n[DHCPv6]\nUseDNS=')
6288 f
.write('yes' if ipv6
else 'no')
6289 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6292 self
.wait_online(['veth99:routable'])
6294 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6295 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6296 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6298 # make resolved re-read the link state file
6299 resolvectl('revert', 'veth99')
6301 output
= resolvectl('dns', 'veth99')
6304 self
.assertIn('192.168.5.1', output
)
6306 self
.assertNotIn('192.168.5.1', output
)
6308 self
.assertIn('2600::1', output
)
6310 self
.assertNotIn('2600::1', output
)
6312 check_json(networkctl_json())
6314 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6317 self
.wait_online(['veth-peer:carrier'])
6318 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6319 '--dhcp-option=option6:dns-server,[2600::1]')
6321 check(self
, True, True)
6322 check(self
, True, False)
6323 check(self
, False, True)
6324 check(self
, False, False)
6326 def test_dhcp_client_use_captive_portal(self
):
6327 def check(self
, ipv4
, ipv6
):
6328 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6329 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6330 f
.write('[DHCPv4]\nUseCaptivePortal=')
6331 f
.write('yes' if ipv4
else 'no')
6332 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6333 f
.write('yes' if ipv6
else 'no')
6334 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6337 self
.wait_online(['veth99:routable'])
6339 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6340 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6341 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6343 output
= networkctl_status('veth99')
6346 self
.assertIn('Captive Portal: http://systemd.io', output
)
6348 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6350 check_json(networkctl_json())
6352 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6355 self
.wait_online(['veth-peer:carrier'])
6356 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6357 '--dhcp-option=option6:103,http://systemd.io')
6359 check(self
, True, True)
6360 check(self
, True, False)
6361 check(self
, False, True)
6362 check(self
, False, False)
6364 def test_dhcp_client_reject_captive_portal(self
):
6365 def check(self
, ipv4
, ipv6
):
6366 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6367 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6368 f
.write('[DHCPv4]\nUseCaptivePortal=')
6369 f
.write('yes' if ipv4
else 'no')
6370 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6371 f
.write('yes' if ipv6
else 'no')
6372 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6375 self
.wait_online(['veth99:routable'])
6377 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6378 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6379 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6381 output
= networkctl_status('veth99')
6383 self
.assertNotIn('Captive Portal: ', output
)
6384 self
.assertNotIn('invalid/url', output
)
6386 check_json(networkctl_json())
6388 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6391 self
.wait_online(['veth-peer:carrier'])
6392 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6393 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6394 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6396 check(self
, True, True)
6397 check(self
, True, False)
6398 check(self
, False, True)
6399 check(self
, False, False)
6401 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6409 def test_dhcp6pd(self
):
6410 def get_dhcp6_prefix(link
):
6411 description
= get_link_description(link
)
6413 self
.assertIn('DHCPv6Client', description
.keys())
6414 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6416 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6420 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6421 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6422 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6423 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6424 '25-dhcp-pd-downstream-dummy97.network',
6425 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6426 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6429 self
.setup_nftset('addr6', 'ipv6_addr')
6430 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6431 self
.setup_nftset('ifindex', 'iface_index')
6434 self
.wait_online(['veth-peer:routable'])
6435 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6436 self
.wait_online(['veth99:degraded'])
6438 # First, test UseAddress=no and Assign=no (issue #29979).
6439 # Note, due to the bug #29701, this test must be done at first.
6440 print('### ip -6 address show dev veth99 scope global')
6441 output
= check_output('ip -6 address show dev veth99 scope global')
6443 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6445 # Check DBus assigned prefix information to veth99
6446 prefixInfo
= get_dhcp6_prefix('veth99')
6448 self
.assertEqual(len(prefixInfo
), 1)
6449 prefixInfo
= prefixInfo
[0]
6451 self
.assertIn('Prefix', prefixInfo
.keys())
6452 self
.assertIn('PrefixLength', prefixInfo
.keys())
6453 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6454 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6456 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6457 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6458 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6459 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6461 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6463 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6464 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6466 print('### ip -6 address show dev veth-peer scope global')
6467 output
= check_output('ip -6 address show dev veth-peer scope global')
6469 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6473 # dummy97: 0x01 (The link will appear later)
6475 # dummy99: auto -> 0x02 (No address assignment)
6480 print('### ip -6 address show dev veth99 scope global')
6481 output
= check_output('ip -6 address show dev veth99 scope global')
6484 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6485 # address in IA_PD (Token=static)
6486 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6487 # address in IA_PD (Token=eui64)
6488 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6489 # address in IA_PD (temporary)
6490 # Note that the temporary addresses may appear after the link enters configured state
6491 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')
6493 print('### ip -6 address show dev test1 scope global')
6494 output
= check_output('ip -6 address show dev test1 scope global')
6496 # address in IA_PD (Token=static)
6497 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6498 # address in IA_PD (temporary)
6499 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')
6501 print('### ip -6 address show dev dummy98 scope global')
6502 output
= check_output('ip -6 address show dev dummy98 scope global')
6504 # address in IA_PD (Token=static)
6505 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6506 # address in IA_PD (temporary)
6507 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')
6509 print('### ip -6 address show dev dummy99 scope global')
6510 output
= check_output('ip -6 address show dev dummy99 scope global')
6513 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6515 print('### ip -6 address show dev veth97 scope global')
6516 output
= check_output('ip -6 address show dev veth97 scope global')
6518 # address in IA_PD (Token=static)
6519 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6520 # address in IA_PD (Token=eui64)
6521 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6522 # address in IA_PD (temporary)
6523 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')
6525 print('### ip -6 address show dev veth97-peer scope global')
6526 output
= check_output('ip -6 address show dev veth97-peer scope global')
6528 # NDisc address (Token=static)
6529 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6530 # NDisc address (Token=eui64)
6531 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6532 # NDisc address (temporary)
6533 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')
6535 print('### ip -6 address show dev veth98 scope global')
6536 output
= check_output('ip -6 address show dev veth98 scope global')
6538 # address in IA_PD (Token=static)
6539 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6540 # address in IA_PD (Token=eui64)
6541 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6542 # address in IA_PD (temporary)
6543 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')
6545 print('### ip -6 address show dev veth98-peer scope global')
6546 output
= check_output('ip -6 address show dev veth98-peer scope global')
6548 # NDisc address (Token=static)
6549 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6550 # NDisc address (Token=eui64)
6551 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6552 # NDisc address (temporary)
6553 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')
6555 print('### ip -6 route show type unreachable')
6556 output
= check_output('ip -6 route show type unreachable')
6558 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6560 print('### ip -6 route show dev veth99')
6561 output
= check_output('ip -6 route show dev veth99')
6563 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6565 print('### ip -6 route show dev test1')
6566 output
= check_output('ip -6 route show dev test1')
6568 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6570 print('### ip -6 route show dev dummy98')
6571 output
= check_output('ip -6 route show dev dummy98')
6573 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6575 print('### ip -6 route show dev dummy99')
6576 output
= check_output('ip -6 route show dev dummy99')
6578 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6580 print('### ip -6 route show dev veth97')
6581 output
= check_output('ip -6 route show dev veth97')
6583 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6585 print('### ip -6 route show dev veth97-peer')
6586 output
= check_output('ip -6 route show dev veth97-peer')
6588 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6590 print('### ip -6 route show dev veth98')
6591 output
= check_output('ip -6 route show dev veth98')
6593 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6595 print('### ip -6 route show dev veth98-peer')
6596 output
= check_output('ip -6 route show dev veth98-peer')
6598 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6600 # Test case for a downstream which appears later
6601 check_output('ip link add dummy97 type dummy')
6602 self
.wait_online(['dummy97:routable'])
6604 print('### ip -6 address show dev dummy97 scope global')
6605 output
= check_output('ip -6 address show dev dummy97 scope global')
6607 # address in IA_PD (Token=static)
6608 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6609 # address in IA_PD (temporary)
6610 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')
6612 print('### ip -6 route show dev dummy97')
6613 output
= check_output('ip -6 route show dev dummy97')
6615 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6617 # Test case for reconfigure
6618 networkctl_reconfigure('dummy98', 'dummy99')
6619 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6621 print('### ip -6 address show dev dummy98 scope global')
6622 output
= check_output('ip -6 address show dev dummy98 scope global')
6624 # address in IA_PD (Token=static)
6625 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6626 # address in IA_PD (temporary)
6627 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')
6629 print('### ip -6 address show dev dummy99 scope global')
6630 output
= check_output('ip -6 address show dev dummy99 scope global')
6633 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6635 print('### ip -6 route show dev dummy98')
6636 output
= check_output('ip -6 route show dev dummy98')
6638 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6640 print('### ip -6 route show dev dummy99')
6641 output
= check_output('ip -6 route show dev dummy99')
6643 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6645 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6647 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6648 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6649 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6650 self
.check_nftset('ifindex', 'dummy98')
6652 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6654 def verify_dhcp4_6rd(self
, tunnel_name
):
6655 print('### ip -4 address show dev veth-peer scope global')
6656 output
= check_output('ip -4 address show dev veth-peer scope global')
6658 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6662 # dummy97: 0x01 (The link will appear later)
6664 # dummy99: auto -> 0x0[23] (No address assignment)
6665 # 6rd-XXX: auto -> 0x0[23]
6670 print('### ip -4 address show dev veth99 scope global')
6671 output
= check_output('ip -4 address show dev veth99 scope global')
6673 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6675 print('### ip -6 address show dev veth99 scope global')
6676 output
= check_output('ip -6 address show dev veth99 scope global')
6678 # address in IA_PD (Token=static)
6679 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6680 # address in IA_PD (Token=eui64)
6681 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6682 # address in IA_PD (temporary)
6683 # Note that the temporary addresses may appear after the link enters configured state
6684 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')
6686 print('### ip -6 address show dev test1 scope global')
6687 output
= check_output('ip -6 address show dev test1 scope global')
6689 # address in IA_PD (Token=static)
6690 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6691 # address in IA_PD (temporary)
6692 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')
6694 print('### ip -6 address show dev dummy98 scope global')
6695 output
= check_output('ip -6 address show dev dummy98 scope global')
6697 # address in IA_PD (Token=static)
6698 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6699 # address in IA_PD (temporary)
6700 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')
6702 print('### ip -6 address show dev dummy99 scope global')
6703 output
= check_output('ip -6 address show dev dummy99 scope global')
6706 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6708 print('### ip -6 address show dev veth97 scope global')
6709 output
= check_output('ip -6 address show dev veth97 scope global')
6711 # address in IA_PD (Token=static)
6712 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6713 # address in IA_PD (Token=eui64)
6714 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6715 # address in IA_PD (temporary)
6716 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')
6718 print('### ip -6 address show dev veth97-peer scope global')
6719 output
= check_output('ip -6 address show dev veth97-peer scope global')
6721 # NDisc address (Token=static)
6722 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6723 # NDisc address (Token=eui64)
6724 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6725 # NDisc address (temporary)
6726 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')
6728 print('### ip -6 address show dev veth98 scope global')
6729 output
= check_output('ip -6 address show dev veth98 scope global')
6731 # address in IA_PD (Token=static)
6732 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6733 # address in IA_PD (Token=eui64)
6734 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6735 # address in IA_PD (temporary)
6736 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')
6738 print('### ip -6 address show dev veth98-peer scope global')
6739 output
= check_output('ip -6 address show dev veth98-peer scope global')
6741 # NDisc address (Token=static)
6742 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6743 # NDisc address (Token=eui64)
6744 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6745 # NDisc address (temporary)
6746 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')
6748 print('### ip -6 route show type unreachable')
6749 output
= check_output('ip -6 route show type unreachable')
6751 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6753 print('### ip -6 route show dev veth99')
6754 output
= check_output('ip -6 route show dev veth99')
6756 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6758 print('### ip -6 route show dev test1')
6759 output
= check_output('ip -6 route show dev test1')
6761 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6763 print('### ip -6 route show dev dummy98')
6764 output
= check_output('ip -6 route show dev dummy98')
6766 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6768 print('### ip -6 route show dev dummy99')
6769 output
= check_output('ip -6 route show dev dummy99')
6771 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6773 print('### ip -6 route show dev veth97')
6774 output
= check_output('ip -6 route show dev veth97')
6776 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6778 print('### ip -6 route show dev veth97-peer')
6779 output
= check_output('ip -6 route show dev veth97-peer')
6781 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6783 print('### ip -6 route show dev veth98')
6784 output
= check_output('ip -6 route show dev veth98')
6786 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6788 print('### ip -6 route show dev veth98-peer')
6789 output
= check_output('ip -6 route show dev veth98-peer')
6791 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6793 print('### ip -6 address show dev dummy97 scope global')
6794 output
= check_output('ip -6 address show dev dummy97 scope global')
6796 # address in IA_PD (Token=static)
6797 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6798 # address in IA_PD (temporary)
6799 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')
6801 print('### ip -6 route show dev dummy97')
6802 output
= check_output('ip -6 route show dev dummy97')
6804 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6806 print(f
'### ip -d link show dev {tunnel_name}')
6807 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6809 self
.assertIn('link/sit 10.100.100.', output
)
6810 self
.assertIn('local 10.100.100.', output
)
6811 self
.assertIn('ttl 64', output
)
6812 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6813 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6815 print(f
'### ip -6 address show dev {tunnel_name}')
6816 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6818 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')
6819 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6821 print(f
'### ip -6 route show dev {tunnel_name}')
6822 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6824 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6825 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6827 print('### ip -6 route show default')
6828 output
= check_output('ip -6 route show default')
6830 self
.assertIn('default', output
)
6831 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6833 def test_dhcp4_6rd(self
):
6834 def get_dhcp_6rd_prefix(link
):
6835 description
= get_link_description(link
)
6837 self
.assertIn('DHCPv4Client', description
.keys())
6838 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6840 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6841 self
.assertIn('Prefix', prefixInfo
.keys())
6842 self
.assertIn('PrefixLength', prefixInfo
.keys())
6843 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6844 self
.assertIn('BorderRouters', prefixInfo
.keys())
6848 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
6849 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6850 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6851 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6852 '25-dhcp-pd-downstream-dummy97.network',
6853 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6854 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6855 '80-6rd-tunnel.network')
6858 self
.wait_online(['veth-peer:routable'])
6861 # 6rd-prefix: 2001:db8::/32
6862 # br-addresss: 10.0.0.1
6864 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',
6865 ipv4_range
='10.100.100.100,10.100.100.200',
6866 ipv4_router
='10.0.0.1')
6867 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6868 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6870 # Check the DBus interface for assigned prefix information
6871 prefixInfo
= get_dhcp_6rd_prefix('veth99')
6873 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
6874 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
6875 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
6876 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
6878 # Test case for a downstream which appears later
6879 check_output('ip link add dummy97 type dummy')
6880 self
.wait_online(['dummy97:routable'])
6884 for name
in os
.listdir('/sys/class/net/'):
6885 if name
.startswith('6rd-'):
6889 self
.wait_online([f
'{tunnel_name}:routable'])
6891 self
.verify_dhcp4_6rd(tunnel_name
)
6893 # Test case for reconfigure
6894 networkctl_reconfigure('dummy98', 'dummy99')
6895 self
.wait_online(['dummy98:routable', 'dummy99:degraded'])
6897 self
.verify_dhcp4_6rd(tunnel_name
)
6899 print('Wait for the DHCP lease to be renewed/rebind')
6902 self
.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
6903 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
6905 self
.verify_dhcp4_6rd(tunnel_name
)
6907 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
6915 def test_ipv6_route_prefix(self
):
6916 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
6917 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6920 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6922 output
= check_output('ip address show dev veth-peer')
6924 self
.assertIn('inet6 2001:db8:0:1:', output
)
6925 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6926 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6928 output
= check_output('ip -6 route show dev veth-peer')
6930 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6931 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6932 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
6933 self
.assertIn('2001:db0:fff::/64 via ', output
)
6934 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6935 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
6937 output
= check_output('ip address show dev veth99')
6939 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6940 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
6941 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
6942 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
6944 output
= resolvectl('dns', 'veth-peer')
6946 self
.assertRegex(output
, '2001:db8:1:1::2')
6948 output
= resolvectl('domain', 'veth-peer')
6950 self
.assertIn('example.com', output
)
6952 check_json(networkctl_json())
6954 output
= networkctl_json('veth-peer')
6958 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
6960 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
6961 self
.assertEqual(prefix
, '64:ff9b::')
6963 prefix_length
= pref64
['PrefixLength']
6964 self
.assertEqual(prefix_length
, 96)
6966 def test_ipv6_route_prefix_deny_list(self
):
6967 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
6968 '12-dummy.netdev', '25-ipv6ra-uplink.network')
6971 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
6973 output
= check_output('ip address show dev veth-peer')
6975 self
.assertIn('inet6 2001:db8:0:1:', output
)
6976 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
6978 output
= check_output('ip -6 route show dev veth-peer')
6980 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
6981 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
6982 self
.assertIn('2001:db0:fff::/64 via ', output
)
6983 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
6985 output
= check_output('ip address show dev veth99')
6987 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
6988 self
.assertIn('inet6 2001:db8:0:2:', output
)
6990 output
= resolvectl('dns', 'veth-peer')
6992 self
.assertRegex(output
, '2001:db8:1:1::2')
6994 output
= resolvectl('domain', 'veth-peer')
6996 self
.assertIn('example.com', output
)
6998 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7006 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7012 self
.wait_online(['dummy98:routable'])
7013 self
.check_link_attr('dummy98', 'mtu', mtu
)
7014 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7016 # test normal restart
7018 self
.wait_online(['dummy98:routable'])
7019 self
.check_link_attr('dummy98', 'mtu', mtu
)
7020 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7023 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7025 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7026 ''' test setting mtu/ipv6_mtu with interface already up '''
7029 # note - changing the device mtu resets the ipv6 mtu
7030 check_output('ip link set up mtu 1501 dev dummy98')
7031 check_output('ip link set up mtu 1500 dev dummy98')
7032 self
.check_link_attr('dummy98', 'mtu', '1500')
7033 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7035 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7037 def test_mtu_network(self
):
7038 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7039 self
.check_mtu('1600')
7041 def test_mtu_netdev(self
):
7042 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7043 # note - MTU set by .netdev happens ONLY at device creation!
7044 self
.check_mtu('1600', reset
=False)
7046 def test_mtu_link(self
):
7047 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7048 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7049 self
.check_mtu('1600', reset
=False)
7051 def test_ipv6_mtu(self
):
7052 ''' set ipv6 mtu without setting device mtu '''
7053 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7054 self
.check_mtu('1500', '1400')
7056 def test_ipv6_mtu_toolarge(self
):
7057 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7058 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7059 self
.check_mtu('1500', '1500')
7061 def test_mtu_network_ipv6_mtu(self
):
7062 ''' set ipv6 mtu and set device mtu via network file '''
7063 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7064 self
.check_mtu('1600', '1550')
7066 def test_mtu_netdev_ipv6_mtu(self
):
7067 ''' set ipv6 mtu and set device mtu via netdev file '''
7068 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7069 self
.check_mtu('1600', '1550', reset
=False)
7071 def test_mtu_link_ipv6_mtu(self
):
7072 ''' set ipv6 mtu and set device mtu via link file '''
7073 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7074 self
.check_mtu('1600', '1550', reset
=False)
7077 if __name__
== '__main__':
7078 parser
= argparse
.ArgumentParser()
7079 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7080 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7081 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7082 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7083 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7084 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7085 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7086 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7087 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7088 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7089 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7090 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7091 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7092 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7093 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7094 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7095 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
)
7096 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7099 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7100 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7101 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7102 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7103 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7104 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7105 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7106 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7107 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7108 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7109 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7110 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7111 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7114 networkd_bin
= ns
.networkd_bin
7116 resolved_bin
= ns
.resolved_bin
7117 if ns
.timesyncd_bin
:
7118 timesyncd_bin
= ns
.timesyncd_bin
7120 udevd_bin
= ns
.udevd_bin
7121 if ns
.wait_online_bin
:
7122 wait_online_bin
= ns
.wait_online_bin
7123 if ns
.networkctl_bin
:
7124 networkctl_bin
= ns
.networkctl_bin
7125 if ns
.resolvectl_bin
:
7126 resolvectl_bin
= ns
.resolvectl_bin
7127 if ns
.timedatectl_bin
:
7128 timedatectl_bin
= ns
.timedatectl_bin
7130 udevadm_bin
= ns
.udevadm_bin
7133 systemd_source_dir
= ns
.source_dir
7135 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7136 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7137 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7139 use_valgrind
= ns
.use_valgrind
7140 enable_debug
= ns
.enable_debug
7141 asan_options
= ns
.asan_options
7142 lsan_options
= ns
.lsan_options
7143 ubsan_options
= ns
.ubsan_options
7144 with_coverage
= ns
.with_coverage
7147 # Do not forget the trailing space.
7148 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7150 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7151 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7152 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7153 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7154 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7157 env
.update({'ASAN_OPTIONS': asan_options
})
7159 env
.update({'LSAN_OPTIONS': lsan_options
})
7161 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7163 env
.update({'SYSTEMD_MEMPOOL': '0'})
7165 wait_online_env
= env
.copy()
7167 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7169 sys
.argv
[1:] = unknown_args
7170 unittest
.main(verbosity
=3)