]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-network/systemd-networkd-tests.py
systemctl: fix log message when glob patterns passed to disable command and friends
[thirdparty/systemd.git] / test / test-network / systemd-networkd-tests.py
CommitLineData
1f0e3109 1#!/usr/bin/env python3
db9ecf05 2# SPDX-License-Identifier: LGPL-2.1-or-later
1f0e3109
SS
3# systemd-networkd tests
4
0f1853e2 5# These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
3ceb96e0 6# simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
d4c8de21
MM
7#
8# To run an individual test, specify it as a command line argument in the form
9# of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
10# function called test_ipv6_mtu(). To run just that test use:
11#
12# sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
13#
68676af6 14# Similarly, other individual tests can be run, eg.:
d4c8de21
MM
15#
16# sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
0f1853e2 17
9c1ae484 18import argparse
b65c5390 19import datetime
dded88ac 20import errno
7c0d36ff 21import itertools
aca99a3a 22import json
1f0e3109 23import os
a962d857 24import pathlib
6d1cea7b 25import random
201bf07f 26import re
1f0e3109
SS
27import shutil
28import signal
681007ac 29import socket
a9bc5e37
YW
30import subprocess
31import sys
a9bc5e37
YW
32import time
33import unittest
a962d857 34
ef11b841
FS
35import psutil
36
a962d857
YW
37network_unit_dir = '/run/systemd/network'
38networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
39networkd_ci_temp_dir = '/run/networkd-ci'
40udev_rules_dir = '/run/udev/rules.d'
fa724cd5 41credstore_dir = '/run/credstore'
a962d857
YW
42
43dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
44dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
45dnsmasq_lease_file = '/run/networkd-ci/test-dnsmasq.lease'
46
47isc_dhcpd_pid_file = '/run/networkd-ci/test-isc-dhcpd.pid'
48isc_dhcpd_lease_file = '/run/networkd-ci/test-isc-dhcpd.lease'
49
c1dd58b3
FS
50radvd_pid_file = '/run/networkd-ci/test-radvd.pid'
51
a962d857
YW
52systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
53which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
54
55networkd_bin = shutil.which('systemd-networkd', path=which_paths)
56resolved_bin = shutil.which('systemd-resolved', path=which_paths)
b05c4d6b 57timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
a962d857
YW
58wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
59networkctl_bin = shutil.which('networkctl', path=which_paths)
60resolvectl_bin = shutil.which('resolvectl', path=which_paths)
61timedatectl_bin = shutil.which('timedatectl', path=which_paths)
b05c4d6b 62udevadm_bin = shutil.which('udevadm', path=which_paths)
9dcdf16b 63test_ndisc_send = None
f66045c7
YW
64build_dir = None
65source_dir = None
a962d857
YW
66
67use_valgrind = False
b05c4d6b 68valgrind_cmd = ''
a962d857 69enable_debug = True
9c1ae484 70env = {}
163d095f 71wait_online_env = {}
a962d857
YW
72asan_options = None
73lsan_options = None
74ubsan_options = None
75with_coverage = False
76
77active_units = []
78protected_links = {
79 'erspan0',
80 'gre0',
81 'gretap0',
82 'ifb0',
83 'ifb1',
84 'ip6_vti0',
85 'ip6gre0',
86 'ip6tnl0',
87 'ip_vti0',
88 'lo',
89 'sit0',
90 'tunl0',
91}
92saved_routes = None
93saved_ipv4_rules = None
94saved_ipv6_rules = None
f54dce2d 95saved_timezone = None
a962d857
YW
96
97def rm_f(path):
98 if os.path.exists(path):
99 os.remove(path)
100
101def rm_rf(path):
102 shutil.rmtree(path, ignore_errors=True)
103
104def cp(src, dst):
105 shutil.copy(src, dst)
106
107def cp_r(src, dst):
108 shutil.copytree(src, dst, copy_function=shutil.copy)
109
110def mkdir_p(path):
111 os.makedirs(path, exist_ok=True)
112
113def touch(path):
114 pathlib.Path(path).touch()
115
f9073c24 116# pylint: disable=R1710
66504b22 117def check_output(*command, **kwargs):
a962d857
YW
118 # This checks the result and returns stdout (and stderr) on success.
119 command = command[0].split() + list(command[1:])
17479d51
YW
120 ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
121 if ret.returncode == 0:
122 return ret.stdout.rstrip()
123 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
124 print(ret.stdout)
125 ret.check_returncode()
2225e7fd 126
66504b22 127def call(*command, **kwargs):
b5dac5b0 128 # This returns returncode. stdout and stderr are merged and shown in console
371810d1 129 command = command[0].split() + list(command[1:])
66504b22 130 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode
371810d1 131
7e107bc3
FS
132def call_check(*command, **kwargs):
133 # Same as call() above, but it triggers CalledProcessError if rc != 0
134 command = command[0].split() + list(command[1:])
135 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).check_returncode()
136
66504b22 137def call_quiet(*command, **kwargs):
371810d1 138 command = command[0].split() + list(command[1:])
66504b22 139 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
371810d1 140
66504b22 141def run(*command, **kwargs):
a962d857 142 # This returns CompletedProcess instance.
371810d1 143 command = command[0].split() + list(command[1:])
66504b22 144 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
371810d1 145
aca99a3a
FS
146def check_json(string):
147 try:
148 json.loads(string)
149 except json.JSONDecodeError:
150 print(f"String is not a valid JSON: '{string}'")
151 raise
152
59edcf2b
YW
153def is_module_available(*module_names):
154 for module_name in module_names:
155 lsmod_output = check_output('lsmod')
156 module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
157 if not module_re.search(lsmod_output) and call_quiet('modprobe', module_name) != 0:
158 return False
159 return True
160
161def expectedFailureIfModuleIsNotAvailable(*module_names):
7a0a37b2 162 def f(func):
59edcf2b 163 return func if is_module_available(*module_names) else unittest.expectedFailure(func)
7a0a37b2
EV
164
165 return f
166
2f0260c1
YW
167def expectedFailureIfERSPANv0IsNotSupported():
168 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
7bea7f9b 169 def f(func):
a962d857
YW
170 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')
171 remove_link('erspan99')
172 return func if rc == 0 else unittest.expectedFailure(func)
2f0260c1
YW
173
174 return f
175
176def expectedFailureIfERSPANv2IsNotSupported():
177 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
178 def f(func):
a962d857
YW
179 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')
180 remove_link('erspan99')
181 return func if rc == 0 else unittest.expectedFailure(func)
7bea7f9b
SS
182
183 return f
184
d586a2c3
YW
185def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
186 def f(func):
a962d857
YW
187 rc = call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
188 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
189 return func if rc == 0 else unittest.expectedFailure(func)
d586a2c3
YW
190
191 return f
192
193def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
194 def f(func):
a962d857
YW
195 rc = call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
196 call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
197 return func if rc == 0 else unittest.expectedFailure(func)
d586a2c3
YW
198
199 return f
200
6be8e78e
YW
201def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
202 def f(func):
a962d857
YW
203 supported = False
204 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
205 ret = run('ip rule list from 192.168.100.19 table 7')
206 supported = ret.returncode == 0 and 'uidrange 200-300' in ret.stdout
207 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
208 return func if supported else unittest.expectedFailure(func)
6be8e78e
YW
209
210 return f
211
4be1fc84
NC
212def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
213 def f(func):
214 rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev')
215 call_quiet('ip rule del not from 192.168.100.19 l3mdev')
216 return func if rc == 0 else unittest.expectedFailure(func)
217
218 return f
219
086bcf5d
YW
220def expectedFailureIfNexthopIsNotAvailable():
221 def f(func):
a962d857
YW
222 rc = call_quiet('ip nexthop list')
223 return func if rc == 0 else unittest.expectedFailure(func)
086bcf5d
YW
224
225 return f
226
297f9d86
YW
227def expectedFailureIfRTA_VIAIsNotSupported():
228 def f(func):
a962d857
YW
229 call_quiet('ip link add dummy98 type dummy')
230 call_quiet('ip link set up dev dummy98')
231 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
232 rc = call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
233 remove_link('dummy98')
234 return func if rc == 0 else unittest.expectedFailure(func)
297f9d86
YW
235
236 return f
237
6934ace0
YW
238def expectedFailureIfAlternativeNameIsNotAvailable():
239 def f(func):
a962d857
YW
240 call_quiet('ip link add dummy98 type dummy')
241 supported = \
242 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
243 call_quiet('ip link show dev hogehogehogehogehoge') == 0
244 remove_link('dummy98')
245 return func if supported else unittest.expectedFailure(func)
6934ace0
YW
246
247 return f
248
3d2c2692
YW
249def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
250 def f(func):
a962d857
YW
251 def finalize(func, supported):
252 call_quiet('rmmod netdevsim')
253 return func if supported else unittest.expectedFailure(func)
254
255 call_quiet('rmmod netdevsim')
256 if call_quiet('modprobe netdevsim') != 0:
257 return finalize(func, False)
3d2c2692
YW
258
259 try:
d45476ef 260 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
3d2c2692 261 f.write('99 1')
54e2f32f 262 except OSError:
a962d857 263 return finalize(func, False)
3d2c2692 264
d8746f16 265 return finalize(func, os.path.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
3d2c2692
YW
266
267 return f
268
b3de9d7b
FS
269# pylint: disable=C0415
270def compare_kernel_version(min_kernel_version):
271 try:
272 import platform
273 from packaging import version
274 except ImportError:
275 print('Failed to import either platform or packaging module, assuming the comparison failed')
276 return False
277
278 # Get only the actual kernel version without any build/distro/arch stuff
279 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
280 kver = platform.release().split('-')[0]
0e808f62
TM
281 # Get also rid of '+'
282 kver = kver.split('+')[0]
b3de9d7b
FS
283
284 return version.parse(kver) >= version.parse(min_kernel_version)
285
a962d857
YW
286def copy_network_unit(*units, copy_dropins=True):
287 """
288 Copy networkd unit files into the testbed.
1f0e3109 289
a962d857 290 Any networkd unit file type can be specified, as well as drop-in files.
1f0e3109 291
a962d857
YW
292 By default, all drop-ins for a specified unit file are copied in;
293 to avoid that specify dropins=False.
3e3b0d2a 294
a962d857
YW
295 When a drop-in file is specified, its unit file is also copied in automatically.
296 """
32ab27af 297 has_link = False
a962d857
YW
298 mkdir_p(network_unit_dir)
299 for unit in units:
300 if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
301 cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
32ab27af 302
a962d857
YW
303 if unit.endswith('.conf'):
304 dropin = unit
305 unit = os.path.dirname(dropin).rstrip('.d')
306 dropindir = os.path.join(network_unit_dir, unit + '.d')
307 mkdir_p(dropindir)
308 cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
32ab27af 309
a962d857
YW
310 cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
311
32ab27af
YW
312 if unit.endswith('.link'):
313 has_link = True
314
315 if has_link:
a39a2a81 316 udevadm_reload()
32ab27af 317
fa724cd5
MY
318def copy_credential(src, target):
319 mkdir_p(credstore_dir)
320 cp(os.path.join(networkd_ci_temp_dir, src),
321 os.path.join(credstore_dir, target))
322
a962d857
YW
323def remove_network_unit(*units):
324 """
325 Remove previously copied unit files from the testbed.
326
327 Drop-ins will be removed automatically.
328 """
32ab27af 329 has_link = False
a962d857
YW
330 for unit in units:
331 rm_f(os.path.join(network_unit_dir, unit))
332 rm_rf(os.path.join(network_unit_dir, unit + '.d'))
333
32ab27af
YW
334 if unit.endswith('.link') or unit.endswith('.link.d'):
335 has_link = True
336
337 if has_link:
a39a2a81 338 udevadm_reload()
32ab27af 339
a962d857 340def clear_network_units():
32ab27af
YW
341 has_link = False
342 if os.path.exists(network_unit_dir):
343 units = os.listdir(network_unit_dir)
344 for unit in units:
345 if unit.endswith('.link') or unit.endswith('.link.d'):
346 has_link = True
347
a962d857
YW
348 rm_rf(network_unit_dir)
349
32ab27af 350 if has_link:
a39a2a81 351 udevadm_reload()
32ab27af 352
a962d857
YW
353def copy_networkd_conf_dropin(*dropins):
354 """Copy networkd.conf dropin files into the testbed."""
355 mkdir_p(networkd_conf_dropin_dir)
356 for dropin in dropins:
357 cp(os.path.join(networkd_ci_temp_dir, dropin), networkd_conf_dropin_dir)
358
359def remove_networkd_conf_dropin(*dropins):
360 """Remove previously copied networkd.conf dropin files from the testbed."""
361 for dropin in dropins:
362 rm_f(os.path.join(networkd_conf_dropin_dir, dropin))
363
364def clear_networkd_conf_dropins():
365 rm_rf(networkd_conf_dropin_dir)
366
33b0e0c0 367def setup_systemd_udev_rules():
f66045c7 368 if not build_dir:
33b0e0c0
FS
369 return
370
371 mkdir_p(udev_rules_dir)
372
f66045c7
YW
373 for path in [build_dir, source_dir]:
374 path = os.path.join(path, "rules.d")
33b0e0c0
FS
375 print(f"Copying udev rules from {path} to {udev_rules_dir}")
376
377 for rule in os.listdir(path):
378 if not rule.endswith(".rules"):
379 continue
380 cp(os.path.join(path, rule), udev_rules_dir)
381
c84a5f5e
YW
382def clear_networkd_state_files():
383 rm_rf('/var/lib/systemd/network/')
384
a962d857
YW
385def copy_udev_rule(*rules):
386 """Copy udev rules"""
387 mkdir_p(udev_rules_dir)
388 for rule in rules:
389 cp(os.path.join(networkd_ci_temp_dir, rule), udev_rules_dir)
390
391def remove_udev_rule(*rules):
392 """Remove previously copied udev rules"""
393 for rule in rules:
394 rm_f(os.path.join(udev_rules_dir, rule))
395
396def clear_udev_rules():
397 rm_rf(udev_rules_dir)
398
399def save_active_units():
87b308c8 400 for u in ['systemd-networkd.socket', 'systemd-networkd.service',
b05c4d6b 401 'systemd-resolved.service', 'systemd-timesyncd.service',
f7ada4b8 402 'firewalld.service']:
2225e7fd 403 if call(f'systemctl is-active --quiet {u}') == 0:
a962d857
YW
404 call(f'systemctl stop {u}')
405 active_units.append(u)
406
407def restore_active_units():
408 if 'systemd-networkd.socket' in active_units:
409 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
a962d857
YW
410 for u in active_units:
411 call(f'systemctl restart {u}')
412
93f5ae6b
YW
413def create_unit_dropin(unit, contents):
414 mkdir_p(f'/run/systemd/system/{unit}.d')
415 with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
416 f.write('\n'.join(contents))
417
62eaf8d0 418def create_service_dropin(service, command, additional_settings=None):
c84a5f5e
YW
419 drop_in = ['[Service]']
420 if command:
421 drop_in += [
422 'ExecStart=',
423 f'ExecStart=!!{valgrind_cmd}{command}',
424 ]
b05c4d6b
YW
425 if enable_debug:
426 drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
427 if asan_options:
428 drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
429 if lsan_options:
430 drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
431 if ubsan_options:
432 drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
433 if asan_options or lsan_options or ubsan_options:
434 drop_in += ['SystemCallFilter=']
435 if use_valgrind or asan_options or lsan_options or ubsan_options:
436 drop_in += ['MemoryDenyWriteExecute=no']
437 if use_valgrind:
438 drop_in += [
439 'Environment=SYSTEMD_MEMPOOL=0',
440 'PrivateTmp=yes',
441 ]
442 if with_coverage:
443 drop_in += [
444 'ProtectSystem=no',
445 'ProtectHome=no',
446 ]
447 if additional_settings:
448 drop_in += additional_settings
449
93f5ae6b 450 create_unit_dropin(f'{service}.service', drop_in)
b05c4d6b 451
83cc1825
YW
452def setup_system_units():
453 if build_dir:
454 mkdir_p('/run/systemd/system/')
455
456 for unit in [
457 'systemd-networkd.service',
458 'systemd-networkd.socket',
c84a5f5e 459 'systemd-networkd-persistent-storage.service',
83cc1825
YW
460 'systemd-resolved.service',
461 'systemd-timesyncd.service',
462 'systemd-udevd.service',
463 ]:
464 for path in [build_dir, source_dir]:
465 fullpath = os.path.join(os.path.join(path, "units"), unit)
466 if os.path.exists(fullpath):
467 print(f"Copying unit file from {fullpath} to /run/systemd/system/")
468 cp(fullpath, '/run/systemd/system/')
469 break
470
471 create_service_dropin('systemd-networkd', networkd_bin,
472 ['[Service]',
473 'Restart=no',
474 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
475 '[Unit]',
476 'StartLimitIntervalSec=0'])
477 create_service_dropin('systemd-resolved', resolved_bin)
478 create_service_dropin('systemd-timesyncd', timesyncd_bin)
479
480 # TODO: also run udevd with sanitizers, valgrind, or coverage
481 create_unit_dropin(
482 'systemd-udevd.service',
483 [
484 '[Service]',
485 'ExecStart=',
486 f'ExecStart=!!@{udevadm_bin} systemd-udevd',
487 ]
488 )
489 create_unit_dropin(
490 'systemd-networkd.socket',
491 [
492 '[Unit]',
493 'StartLimitIntervalSec=0',
494 ]
495 )
c84a5f5e
YW
496 create_unit_dropin(
497 'systemd-networkd-persistent-storage.service',
498 [
499 '[Unit]',
500 'StartLimitIntervalSec=0',
501 '[Service]',
502 'ExecStart=',
503 f'ExecStart={networkctl_bin} persistent-storage yes',
504 'ExecStop=',
505 f'ExecStop={networkctl_bin} persistent-storage no'
506 ]
507 )
83cc1825
YW
508
509 check_output('systemctl daemon-reload')
510 print(check_output('systemctl cat systemd-networkd.service'))
c84a5f5e 511 print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
83cc1825
YW
512 print(check_output('systemctl cat systemd-resolved.service'))
513 print(check_output('systemctl cat systemd-timesyncd.service'))
514 print(check_output('systemctl cat systemd-udevd.service'))
515 check_output('systemctl restart systemd-resolved.service')
516 check_output('systemctl restart systemd-timesyncd.service')
517 check_output('systemctl restart systemd-udevd.service')
518
519def clear_system_units():
520 def rm_unit(name):
521 rm_f(f'/run/systemd/system/{name}')
522 rm_rf(f'/run/systemd/system/{name}.d')
523
524 rm_unit('systemd-networkd.service')
525 rm_unit('systemd-networkd.socket')
c84a5f5e 526 rm_unit('systemd-networkd-persistent-storage.service')
83cc1825
YW
527 rm_unit('systemd-resolved.service')
528 rm_unit('systemd-timesyncd.service')
529 rm_unit('systemd-udevd.service')
530 check_output('systemctl daemon-reload')
531 check_output('systemctl restart systemd-udevd.service')
532
a962d857 533def link_exists(link):
dee6c26f 534 return call_quiet(f'ip link show {link}') == 0
a962d857 535
b95d35b5
YW
536def link_resolve(link):
537 return check_output(f'ip link show {link}').split(':')[1].strip()
a962d857
YW
538
539def remove_link(*links, protect=False):
540 for link in links:
541 if protect and link in protected_links:
542 continue
543 if link_exists(link):
544 call(f'ip link del dev {link}')
545
546def save_existing_links():
547 links = os.listdir('/sys/class/net')
548 for link in links:
549 if link_exists(link):
550 protected_links.add(link)
551
552 print('### The following links will be protected:')
553 print(', '.join(sorted(list(protected_links))))
554
555def flush_links():
556 links = os.listdir('/sys/class/net')
557 remove_link(*links, protect=True)
558
559def flush_nexthops():
560 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
561 # Hence, we cannot restore nexthops in a simple way.
562 # Let's assume there is no nexthop used in the system
563 call_quiet('ip nexthop flush')
564
565def save_routes():
566 # pylint: disable=global-statement
567 global saved_routes
568 saved_routes = check_output('ip route show table all')
569 print('### The following routes will be protected:')
570 print(saved_routes)
571
572def flush_routes():
573 have = False
574 output = check_output('ip route show table all')
575 for line in output.splitlines():
576 if line in saved_routes:
577 continue
578 if 'proto kernel' in line:
579 continue
580 if ' dev ' in line and not ' dev lo ' in line:
581 continue
582 if not have:
583 have = True
584 print('### Removing routes that did not exist when the test started.')
585 print(f'# {line}')
586 call(f'ip route del {line}')
587
588def save_routing_policy_rules():
589 # pylint: disable=global-statement
590 global saved_ipv4_rules, saved_ipv6_rules
591 def save(ipv):
592 output = check_output(f'ip -{ipv} rule show')
593 print(f'### The following IPv{ipv} routing policy rules will be protected:')
594 print(output)
595 return output
596
597 saved_ipv4_rules = save(4)
598 saved_ipv6_rules = save(6)
599
600def flush_routing_policy_rules():
601 def flush(ipv, saved_rules):
602 have = False
603 output = check_output(f'ip -{ipv} rule show')
604 for line in output.splitlines():
605 if line in saved_rules:
606 continue
607 if not have:
608 have = True
609 print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
610 print(f'# {line}')
e755ad61 611 words = line.replace('lookup [l3mdev-table]', 'l3mdev').split()
a962d857
YW
612 priority = words[0].rstrip(':')
613 call(f'ip -{ipv} rule del priority {priority} ' + ' '.join(words[1:]))
614
615 flush(4, saved_ipv4_rules)
616 flush(6, saved_ipv6_rules)
617
618def flush_fou_ports():
619 ret = run('ip fou show')
620 if ret.returncode != 0:
621 return # fou may not be supported
622 for line in ret.stdout.splitlines():
623 port = line.split()[1]
624 call(f'ip fou del port {port}')
625
626def flush_l2tp_tunnels():
e11d0e39 627 tids = []
49ad2872
YW
628 ret = run('ip l2tp show tunnel')
629 if ret.returncode != 0:
630 return # l2tp may not be supported
631 for line in ret.stdout.splitlines():
a962d857
YW
632 words = line.split()
633 if words[0] == 'Tunnel':
634 tid = words[1].rstrip(',')
635 call(f'ip l2tp del tunnel tunnel_id {tid}')
e11d0e39
YW
636 tids.append(tid)
637
638 # Removing L2TP tunnel is asynchronous and slightly takes a time.
639 for tid in tids:
640 for _ in range(50):
641 r = run(f'ip l2tp show tunnel tunnel_id {tid}')
642 if r.returncode != 0 or len(r.stdout.rstrip()) == 0:
643 break
644 time.sleep(.2)
645 else:
646 print(f'Cannot remove L2TP tunnel {tid}, ignoring.')
a962d857 647
f54dce2d 648def save_timezone():
f9073c24 649 # pylint: disable=global-statement
f54dce2d 650 global saved_timezone
67a9b3ec 651 r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
f54dce2d
YW
652 if r.returncode == 0:
653 saved_timezone = r.stdout.rstrip()
654 print(f'### Saved timezone: {saved_timezone}')
655
656def restore_timezone():
657 if saved_timezone:
67a9b3ec 658 call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
f54dce2d 659
a962d857
YW
660def read_link_attr(*args):
661 with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
662 return f.readline().strip()
663
34290c6a
YW
664def read_manager_state_file():
665 with open('/run/systemd/netif/state', encoding='utf-8') as f:
666 return f.read()
667
a962d857
YW
668def read_link_state_file(link):
669 ifindex = read_link_attr(link, 'ifindex')
670 path = os.path.join('/run/systemd/netif/links', ifindex)
671 with open(path, encoding='utf-8') as f:
672 return f.read()
673
674def read_ip_sysctl_attr(link, attribute, ipv):
675 with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f:
676 return f.readline().strip()
677
d4c8de21
MM
678def read_ip_neigh_sysctl_attr(link, attribute, ipv):
679 with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f:
680 return f.readline().strip()
681
a962d857
YW
682def read_ipv6_sysctl_attr(link, attribute):
683 return read_ip_sysctl_attr(link, attribute, 'ipv6')
684
d4c8de21
MM
685def read_ipv6_neigh_sysctl_attr(link, attribute):
686 return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6')
687
a962d857
YW
688def read_ipv4_sysctl_attr(link, attribute):
689 return read_ip_sysctl_attr(link, attribute, 'ipv4')
690
c1dd58b3
FS
691def stop_by_pid_file(pid_file):
692 if not os.path.exists(pid_file):
693 return
694 with open(pid_file, 'r', encoding='utf-8') as f:
695 pid = f.read().rstrip(' \t\r\n\0')
696 os.kill(int(pid), signal.SIGTERM)
697 for _ in range(25):
698 try:
699 os.kill(int(pid), 0)
700 print(f"PID {pid} is still alive, waiting...")
701 time.sleep(.2)
702 except OSError as e:
703 if e.errno == errno.ESRCH:
704 break
705 print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
706 rm_f(pid_file)
707
2d7ca6b4
YW
708def 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'):
709 if ra_mode:
710 ra_mode = f',{ra_mode}'
711 else:
712 ra_mode = ''
713
a962d857
YW
714 command = (
715 'dnsmasq',
716 f'--log-facility={dnsmasq_log_file}',
717 '--log-queries=extra',
718 '--log-dhcp',
719 f'--pid-file={dnsmasq_pid_file}',
720 '--conf-file=/dev/null',
721 '--bind-interfaces',
722 f'--interface={interface}',
723 f'--dhcp-leasefile={dnsmasq_lease_file}',
724 '--enable-ra',
2d7ca6b4
YW
725 f'--dhcp-range={ipv6_range}{ra_mode},2m',
726 f'--dhcp-range={ipv4_range},2m',
a962d857
YW
727 '--dhcp-option=option:mtu,1492',
728 f'--dhcp-option=option:router,{ipv4_router}',
729 '--port=0',
730 '--no-resolv',
731 ) + additional_options
732 check_output(*command)
733
a962d857
YW
734def stop_dnsmasq():
735 stop_by_pid_file(dnsmasq_pid_file)
736 rm_f(dnsmasq_lease_file)
737 rm_f(dnsmasq_log_file)
738
739def read_dnsmasq_log_file():
740 with open(dnsmasq_log_file, encoding='utf-8') as f:
741 return f.read()
742
743def start_isc_dhcpd(conf_file, ipv, interface='veth-peer'):
744 conf_file_path = os.path.join(networkd_ci_temp_dir, conf_file)
745 isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
746 touch(isc_dhcpd_lease_file)
747 check_output(isc_dhcpd_command)
748
749def stop_isc_dhcpd():
750 stop_by_pid_file(isc_dhcpd_pid_file)
751 rm_f(isc_dhcpd_lease_file)
752
e081ffc1
YW
753def get_dbus_link_path(link):
754 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
755 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
756 'GetLinkByName', 's', link])
757
758 assert out.startswith(b'io ')
759 out = out.strip()
760 assert out.endswith(b'"')
761 out = out.decode()
762 return out[:-1].split('"')[1]
763
764def get_dhcp_client_state(link, family):
765 link_path = get_dbus_link_path(link)
766
767 out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
768 link_path, f'org.freedesktop.network1.DHCPv{family}Client', 'State'])
769 assert out.startswith(b's "')
770 out = out.strip()
771 assert out.endswith(b'"')
772 return out[3:-1].decode()
773
774def get_dhcp4_client_state(link):
775 return get_dhcp_client_state(link, '4')
776
777def get_dhcp6_client_state(link):
778 return get_dhcp_client_state(link, '6')
779
780def get_link_description(link):
781 link_path = get_dbus_link_path(link)
782
783 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
784 link_path, 'org.freedesktop.network1.Link', 'Describe'])
785 assert out.startswith(b's "')
786 out = out.strip()
787 assert out.endswith(b'"')
788 json_raw = out[2:].decode()
789 check_json(json_raw)
790 description = json.loads(json_raw) # Convert from escaped sequences to json
791 check_json(description)
792 return json.loads(description) # Now parse the json
793
c1dd58b3
FS
794def start_radvd(*additional_options, config_file):
795 config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file)
796 command = (
797 'radvd',
798 f'--pidfile={radvd_pid_file}',
799 f'--config={config_file_path}',
800 '--logmethod=stderr',
801 ) + additional_options
802 check_output(*command)
803
804def stop_radvd():
805 stop_by_pid_file(radvd_pid_file)
806
807def radvd_check_config(config_file):
808 if not shutil.which('radvd'):
809 print('radvd is not installed, assuming the config check failed')
810 return False
811
812 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
813 # set up (one instance is @unittest.skipX())
814 config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file)
815 return call(f'radvd --config={config_file_path} --configtest') == 0
816
5bd2a7c5
YW
817def networkd_invocation_id():
818 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
819
b65c5390 820def read_networkd_log(invocation_id=None, since=None):
5bd2a7c5
YW
821 if not invocation_id:
822 invocation_id = networkd_invocation_id()
b65c5390
YW
823 command = [
824 'journalctl',
dc60ac29
YW
825 '--no-hostname',
826 '--output=short-monotonic',
b65c5390
YW
827 f'_SYSTEMD_INVOCATION_ID={invocation_id}',
828 ]
829 if since:
830 command.append(f'--since={since}')
bd581438 831 check_output('journalctl --sync')
b65c5390 832 return check_output(*command)
5bd2a7c5 833
6b07675d
YW
834def networkd_is_failed():
835 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
836
a962d857
YW
837def stop_networkd(show_logs=True):
838 if show_logs:
5bd2a7c5 839 invocation_id = networkd_invocation_id()
a962d857
YW
840 check_output('systemctl stop systemd-networkd.socket')
841 check_output('systemctl stop systemd-networkd.service')
842 if show_logs:
5bd2a7c5 843 print(read_networkd_log(invocation_id))
7618fd06 844 # Check if networkd exits cleanly.
6b07675d 845 assert not networkd_is_failed()
a962d857
YW
846
847def start_networkd():
848 check_output('systemctl start systemd-networkd')
849
850def restart_networkd(show_logs=True):
85b1a14d 851 if show_logs:
5bd2a7c5 852 invocation_id = networkd_invocation_id()
85b1a14d
YW
853 check_output('systemctl restart systemd-networkd.service')
854 if show_logs:
5bd2a7c5 855 print(read_networkd_log(invocation_id))
a962d857 856
ae014ecb
YW
857def networkd_pid():
858 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
859
10d670a3 860def networkctl(*args):
6b07675d
YW
861 # Do not call networkctl if networkd is in failed state.
862 # Otherwise, networkd may be restarted and we may get wrong results.
863 assert not networkd_is_failed()
10d670a3
YW
864 return check_output(*(networkctl_cmd + list(args)), env=env)
865
866def networkctl_status(*args):
867 return networkctl('-n', '0', 'status', *args)
868
869def networkctl_json(*args):
870 return networkctl('--json=short', 'status', *args)
871
a962d857 872def networkctl_reconfigure(*links):
10d670a3 873 networkctl('reconfigure', *links)
a962d857 874
4bc771d0 875def networkctl_reload():
10d670a3 876 networkctl('reload')
a962d857 877
10d670a3
YW
878def resolvectl(*args):
879 return check_output(*(resolvectl_cmd + list(args)), env=env)
880
881def timedatectl(*args):
882 return check_output(*(timedatectl_cmd + list(args)), env=env)
883
a39a2a81
YW
884def udevadm(*args):
885 return check_output(*(udevadm_cmd + list(args)))
886
887def udevadm_reload():
888 udevadm('control', '--reload')
889
890def udevadm_trigger(*args, action='add'):
891 udevadm('trigger', '--settle', f'--action={action}', *args)
892
a962d857
YW
893def setup_common():
894 print()
895
896def tear_down_common():
c1dd58b3 897 # 1. stop DHCP/RA servers
a962d857
YW
898 stop_dnsmasq()
899 stop_isc_dhcpd()
c1dd58b3 900 stop_radvd()
a962d857
YW
901
902 # 2. remove modules
903 call_quiet('rmmod netdevsim')
904 call_quiet('rmmod sch_teql')
905
906 # 3. remove network namespace
907 call_quiet('ip netns del ns99')
908
909 # 4. remove links
e11d0e39 910 flush_l2tp_tunnels()
a962d857
YW
911 flush_links()
912
913 # 5. stop networkd
249b7ecc 914 stop_networkd()
a962d857
YW
915
916 # 6. remove configs
917 clear_network_units()
918 clear_networkd_conf_dropins()
c84a5f5e 919 clear_networkd_state_files()
a962d857
YW
920
921 # 7. flush settings
922 flush_fou_ports()
a962d857
YW
923 flush_nexthops()
924 flush_routing_policy_rules()
925 flush_routes()
926
927def setUpModule():
928 rm_rf(networkd_ci_temp_dir)
929 cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir)
930
931 clear_network_units()
932 clear_networkd_conf_dropins()
c84a5f5e 933 clear_networkd_state_files()
a962d857
YW
934 clear_udev_rules()
935
33b0e0c0 936 setup_systemd_udev_rules()
a962d857
YW
937 copy_udev_rule('00-debug-net.rules')
938
939 # Save current state
940 save_active_units()
941 save_existing_links()
942 save_routes()
943 save_routing_policy_rules()
f54dce2d 944 save_timezone()
c0bf6733 945
83cc1825 946 setup_system_units()
9c1ae484 947
1f0e3109 948def tearDownModule():
a962d857
YW
949 rm_rf(networkd_ci_temp_dir)
950 clear_udev_rules()
951 clear_network_units()
952 clear_networkd_conf_dropins()
c84a5f5e 953 clear_networkd_state_files()
a962d857 954
f54dce2d
YW
955 restore_timezone()
956
83cc1825 957 clear_system_units()
a962d857 958 restore_active_units()
ec38833c 959
a962d857
YW
960class Utilities():
961 # pylint: disable=no-member
caad88a2 962
a962d857
YW
963 def check_link_exists(self, link, expected=True):
964 if expected:
965 self.assertTrue(link_exists(link))
966 else:
967 self.assertFalse(link_exists(link))
caad88a2 968
a962d857
YW
969 def check_link_attr(self, *args):
970 self.assertEqual(read_link_attr(*args[:-1]), args[-1])
aaae5713 971
a962d857
YW
972 def check_bridge_port_attr(self, master, port, attribute, expected, allow_enoent=False):
973 path = os.path.join('/sys/devices/virtual/net', master, 'lower_' + port, 'brport', attribute)
974 if allow_enoent and not os.path.exists(path):
975 return
976 with open(path, encoding='utf-8') as f:
977 self.assertEqual(f.readline().strip(), expected)
aaae5713 978
a962d857
YW
979 def check_ipv4_sysctl_attr(self, link, attribute, expected):
980 self.assertEqual(read_ipv4_sysctl_attr(link, attribute), expected)
ec38833c 981
a962d857
YW
982 def check_ipv6_sysctl_attr(self, link, attribute, expected):
983 self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected)
aaae5713 984
d4c8de21
MM
985 def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
986 self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
987
a962d857
YW
988 def wait_links(self, *links, timeout=20, fail_assert=True):
989 def links_exist(*links):
990 for link in links:
991 if not link_exists(link):
992 return False
993 return True
524cc9d1 994
a962d857
YW
995 for iteration in range(timeout + 1):
996 if iteration > 0:
997 time.sleep(1)
2be0b6fc 998
a962d857
YW
999 if links_exist(*links):
1000 return True
1001 if fail_assert:
1002 self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links)))
1003 return False
0fc0d85f 1004
cfbdc438
YW
1005 def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
1006 # wait for the interface is activated.
cfbdc438
YW
1007 needle = f'{link}: Bringing link {state}'
1008 flag = state.upper()
a962d857 1009 for iteration in range(timeout + 1):
459c35d4
YW
1010 if iteration != 0:
1011 time.sleep(1)
1012 if not link_exists(link):
1013 continue
032fd10d 1014 output = read_networkd_log()
cfbdc438
YW
1015 if needle in output and flag in check_output(f'ip link show {link}'):
1016 return True
cfbdc438
YW
1017 if fail_assert:
1018 self.fail(f'Timed out waiting for {link} activated.')
1019 return False
1020
a4632dc7
DS
1021 def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
1022 """Wait for the link to reach the specified operstate and/or setup state.
1023
1024 Specify None or '' for either operstate or setup_state to ignore that state.
1025 This will recheck until the state conditions are met or the timeout expires.
1026
1027 If the link successfully matches the requested state, this returns True.
1028 If this times out waiting for the link to match, the behavior depends on the
1029 'fail_assert' parameter; if True, this causes a test assertion failure,
1030 otherwise this returns False. The default is to cause assertion failure.
1031
1032 Note that this function matches on *exactly* the given operstate and setup_state.
1033 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1034 """
1035 if not operstate:
1036 operstate = r'\S+'
1037 if not setup_state:
1038 setup_state = r'\S+'
1039
1040 for secs in range(setup_timeout + 1):
459c35d4
YW
1041 if secs != 0:
1042 time.sleep(1)
1043 if not link_exists(link):
1044 continue
10d670a3 1045 output = networkctl_status(link)
a4632dc7
DS
1046 if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
1047 return True
894ff7d1 1048
a4632dc7
DS
1049 if fail_assert:
1050 self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1051 return False
2be0b6fc 1052
95e1fbba 1053 def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
0923b425 1054 """Wait for the links to reach the specified operstate and/or setup state.
0c020321
DS
1055
1056 This is similar to wait_operstate() but can be used for multiple links,
1057 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1058 The operstate should be specified in the link name, like 'eth0:degraded'.
1059 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1060
1061 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1062 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1063
1064 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1065 If this is set, no setup_state checks are done.
1066
70448bb1
L
1067 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1068 This is applied only for the operational state 'degraded' or above.
1069
0923b425 1070 Note that this function waits for the links to reach *or exceed* the given operstate.
0c020321
DS
1071 However, the setup_state, if specified, must be matched *exactly*.
1072
0923b425 1073 This returns if the links reached the requested operstate/setup_state; otherwise it
0c020321
DS
1074 raises CalledProcessError or fails test assertion.
1075 """
56dfde0d 1076 args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + [f'--ignore={link}' for link in protected_links]
e2aea43f
YW
1077 if bool_any:
1078 args += ['--any']
70448bb1
L
1079 if ipv4:
1080 args += ['--ipv4']
1081 if ipv6:
1082 args += ['--ipv6']
e2aea43f 1083 try:
163d095f 1084 check_output(*args, env=wait_online_env)
f9073c24 1085 except subprocess.CalledProcessError:
6b07675d
YW
1086 if networkd_is_failed():
1087 print('!!!!! systemd-networkd.service is failed !!!!!')
1088 call('systemctl status systemd-networkd.service')
1089 else:
1090 # show detailed status on failure
1091 for link in links_with_operstate:
1092 name = link.split(':')[0]
1093 if link_exists(name):
68543224
YW
1094 print(networkctl_status(name))
1095 else:
1096 print(f'Interface {name} not found.')
e2aea43f 1097 raise
fd372b1a 1098 if not bool_any and setup_state:
0c020321
DS
1099 for link in links_with_operstate:
1100 self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
e2aea43f 1101
53c32c2b 1102 def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
2629df47
YW
1103 for i in range(timeout_sec):
1104 if i > 0:
1105 time.sleep(1)
371810d1 1106 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
571f9539 1107 if re.search(address_regex, output) and 'tentative' not in output:
2629df47 1108 break
240e4137
YW
1109
1110 self.assertRegex(output, address_regex)
1111
1112 def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
1113 for i in range(timeout_sec):
1114 if i > 0:
1115 time.sleep(1)
1116 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1117 if not re.search(address_regex, output):
1118 break
1119
1120 self.assertNotRegex(output, address_regex)
2629df47 1121
bb80f633
YW
1122 def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1123 for i in range(timeout_sec):
1124 if i > 0:
1125 time.sleep(1)
1126 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1127 if re.search(route_regex, output):
1128 break
1129
1130 self.assertRegex(output, route_regex)
1131
9dcdf16b
YW
1132 def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1133 for i in range(timeout_sec):
1134 if i > 0:
1135 time.sleep(1)
1136 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1137 if not re.search(route_regex, output):
1138 break
1139
1140 self.assertNotRegex(output, route_regex)
1141
a4640bed
TM
1142 def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
1143 if not shutil.which('selinuxenabled'):
e4377659 1144 print('## Checking NetLabel skipped: selinuxenabled command not found.')
a4640bed 1145 elif call_quiet('selinuxenabled') != 0:
e4377659 1146 print('## Checking NetLabel skipped: SELinux disabled.')
a4640bed 1147 elif not shutil.which('netlabelctl'): # not packaged by all distros
e4377659 1148 print('## Checking NetLabel skipped: netlabelctl command not found.')
a4640bed
TM
1149 else:
1150 output = check_output('netlabelctl unlbl list')
1151 print(output)
1152 self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
1153
c742d7e8
TM
1154 def setup_nftset(self, filter_name, filter_type, flags=''):
1155 if not shutil.which('nft'):
1156 print('## Setting up NFT sets skipped: nft command not found.')
1157 else:
1158 if call(f'nft add table inet sd_test') != 0:
1159 print('## Setting up NFT table failed.')
1160 self.fail()
1161 if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1162 print('## Setting up NFT sets failed.')
1163 self.fail()
1164
1165 def teardown_nftset(self, *filters):
1166 if not shutil.which('nft'):
1167 print('## Tearing down NFT sets skipped: nft command not found.')
1168 else:
1169 for filter_name in filters:
1170 if call(f'nft delete set inet sd_test {filter_name}') != 0:
1171 print('## Tearing down NFT sets failed.')
1172 self.fail()
1173 if call(f'nft delete table inet sd_test') != 0:
1174 print('## Tearing down NFT table failed.')
1175 self.fail()
1176
1177 def check_nftset(self, filter_name, contents):
1178 if not shutil.which('nft'):
1179 print('## Checking NFT sets skipped: nft command not found.')
1180 else:
1181 output = check_output(f'nft list set inet sd_test {filter_name}')
1182 print(output)
1183 self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
1184
45c2bbba
YW
1185 def check_networkd_log(self, contents, since=None, trial=20):
1186 for _ in range(trial):
1187 if contents in read_networkd_log(since=since):
1188 break
1189 time.sleep(0.5)
1190 else:
1191 self.fail(f'"{contents}" not found in journal.')
1192
1ca44d7d
YW
1193class NetworkctlTests(unittest.TestCase, Utilities):
1194
1ca44d7d 1195 def setUp(self):
a962d857 1196 setup_common()
1ca44d7d
YW
1197
1198 def tearDown(self):
a962d857 1199 tear_down_common()
1ca44d7d 1200
6934ace0
YW
1201 @expectedFailureIfAlternativeNameIsNotAvailable()
1202 def test_altname(self):
a962d857 1203 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
6934ace0 1204 start_networkd()
95e1fbba 1205 self.wait_online('dummy98:degraded')
6934ace0 1206
10d670a3 1207 output = networkctl_status('dummy98')
6934ace0
YW
1208 self.assertRegex(output, 'hogehogehogehogehogehoge')
1209
f68f644a
NR
1210 @expectedFailureIfAlternativeNameIsNotAvailable()
1211 def test_rename_to_altname(self):
1212 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1213 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1214 start_networkd()
95e1fbba 1215 self.wait_online('dummyalt:degraded')
f68f644a 1216
10d670a3 1217 output = networkctl_status('dummyalt')
f68f644a
NR
1218 self.assertIn('hogehogehogehogehogehoge', output)
1219 self.assertNotIn('dummy98', output)
1220
dcd9f07c 1221 def test_reconfigure(self):
67150a7b 1222 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
dcd9f07c 1223 start_networkd()
95e1fbba 1224 self.wait_online('dummy98:routable')
dcd9f07c
YW
1225
1226 output = check_output('ip -4 address show dev dummy98')
1227 print(output)
3bad5487
YW
1228 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1229 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1230 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
dcd9f07c
YW
1231
1232 check_output('ip address del 10.1.2.3/16 dev dummy98')
1233 check_output('ip address del 10.1.2.4/16 dev dummy98')
1234 check_output('ip address del 10.2.2.4/16 dev dummy98')
1235
a962d857 1236 networkctl_reconfigure('dummy98')
95e1fbba 1237 self.wait_online('dummy98:routable')
dcd9f07c
YW
1238
1239 output = check_output('ip -4 address show dev dummy98')
1240 print(output)
3bad5487
YW
1241 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1242 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1243 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1244
a962d857 1245 remove_network_unit('25-address-static.network')
3bad5487 1246
a962d857 1247 networkctl_reload()
3bad5487
YW
1248 self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
1249
1250 output = check_output('ip -4 address show dev dummy98')
1251 print(output)
1252 self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1253 self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1254 self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1255
67150a7b 1256 copy_network_unit('25-address-static.network', copy_dropins=False)
a962d857 1257 networkctl_reload()
95e1fbba 1258 self.wait_online('dummy98:routable')
3bad5487
YW
1259
1260 output = check_output('ip -4 address show dev dummy98')
1261 print(output)
1262 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1263 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1264 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
dcd9f07c 1265
7e107bc3
FS
1266 def test_renew(self):
1267 def check():
95e1fbba 1268 self.wait_online('veth99:routable', 'veth-peer:routable')
10d670a3 1269 output = networkctl_status('veth99')
7e107bc3 1270 print(output)
e6c4b5dc 1271 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7e107bc3
FS
1272 self.assertIn('Gateway: 192.168.5.3', output)
1273 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
1274 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
1275
1276 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1277 start_networkd()
1278 check()
10d670a3 1279 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
7e107bc3
FS
1280
1281 for verb in ['renew', 'forcerenew']:
10d670a3 1282 networkctl(verb, 'veth99')
7e107bc3 1283 check()
10d670a3 1284 networkctl(verb, 'veth99', 'veth99', 'veth99')
7e107bc3
FS
1285 check()
1286
1287 def test_up_down(self):
67150a7b 1288 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
7e107bc3 1289 start_networkd()
95e1fbba 1290 self.wait_online('dummy98:routable')
7e107bc3 1291
10d670a3 1292 networkctl('down', 'dummy98')
95e1fbba 1293 self.wait_online('dummy98:off')
10d670a3 1294 networkctl('up', 'dummy98')
95e1fbba 1295 self.wait_online('dummy98:routable')
10d670a3 1296 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
95e1fbba 1297 self.wait_online('dummy98:off')
10d670a3 1298 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
95e1fbba 1299 self.wait_online('dummy98:routable')
7e107bc3 1300
66de8671 1301 def test_reload(self):
a962d857 1302 start_networkd()
66de8671 1303
a962d857
YW
1304 copy_network_unit('11-dummy.netdev')
1305 networkctl_reload()
19cf3143 1306 self.wait_operstate('test1', 'off', setup_state='unmanaged')
66de8671 1307
a962d857
YW
1308 copy_network_unit('11-dummy.network')
1309 networkctl_reload()
95e1fbba 1310 self.wait_online('test1:degraded')
66de8671 1311
a962d857
YW
1312 remove_network_unit('11-dummy.network')
1313 networkctl_reload()
19cf3143 1314 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
66de8671 1315
a962d857
YW
1316 remove_network_unit('11-dummy.netdev')
1317 networkctl_reload()
19cf3143 1318 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
66de8671 1319
a962d857
YW
1320 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1321 networkctl_reload()
19cf3143 1322 self.wait_operstate('test1', 'degraded')
66de8671 1323
1ca44d7d 1324 def test_glob(self):
a962d857 1325 copy_network_unit('11-dummy.netdev', '11-dummy.network')
2cf6fdff 1326 start_networkd()
1ca44d7d 1327
95e1fbba 1328 self.wait_online('test1:degraded')
1ca44d7d 1329
10d670a3 1330 output = networkctl('list')
1ca44d7d
YW
1331 self.assertRegex(output, '1 lo ')
1332 self.assertRegex(output, 'test1')
1333
10d670a3 1334 output = networkctl('list', 'test1')
1ca44d7d
YW
1335 self.assertNotRegex(output, '1 lo ')
1336 self.assertRegex(output, 'test1')
1337
10d670a3 1338 output = networkctl('list', 'te*')
1ca44d7d
YW
1339 self.assertNotRegex(output, '1 lo ')
1340 self.assertRegex(output, 'test1')
1341
10d670a3 1342 output = networkctl_status('te*')
1ca44d7d
YW
1343 self.assertNotRegex(output, '1: lo ')
1344 self.assertRegex(output, 'test1')
1345
10d670a3 1346 output = networkctl_status('tes[a-z][0-9]')
1ca44d7d
YW
1347 self.assertNotRegex(output, '1: lo ')
1348 self.assertRegex(output, 'test1')
1349
6d5b4efe 1350 def test_mtu(self):
a962d857 1351 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
2cf6fdff 1352 start_networkd()
6d5b4efe 1353
95e1fbba 1354 self.wait_online('test1:degraded')
6d5b4efe 1355
10d670a3 1356 output = networkctl_status('test1')
6d5b4efe
YW
1357 self.assertRegex(output, 'MTU: 1600')
1358
e28fd95f 1359 def test_type(self):
a962d857 1360 copy_network_unit('11-dummy.netdev', '11-dummy.network')
e28fd95f 1361 start_networkd()
95e1fbba 1362 self.wait_online('test1:degraded')
e28fd95f 1363
10d670a3 1364 output = networkctl_status('test1')
e28fd95f
YW
1365 print(output)
1366 self.assertRegex(output, 'Type: ether')
1367
10d670a3 1368 output = networkctl_status('lo')
e28fd95f
YW
1369 print(output)
1370 self.assertRegex(output, 'Type: loopback')
1371
b993e7e7
YW
1372 def test_unit_file(self):
1373 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
e28fd95f 1374 start_networkd()
95e1fbba 1375 self.wait_online('test1:degraded')
e28fd95f 1376
10d670a3 1377 output = networkctl_status('test1')
e28fd95f 1378 print(output)
b993e7e7
YW
1379 self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
1380 self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
1381 self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
1382 self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
1383
45c2bbba 1384 self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).')
e28fd95f 1385
df0a741c
YW
1386 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1387 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1388 # Let's reprocess the interface and drop the property.
a39a2a81 1389 udevadm_trigger('/sys/class/net/lo')
10d670a3 1390 output = networkctl_status('lo')
e28fd95f 1391 print(output)
b993e7e7
YW
1392 self.assertIn('Link File: n/a', output)
1393 self.assertIn('Network File: n/a', output)
e28fd95f 1394
bee692fd 1395 def test_delete_links(self):
a962d857
YW
1396 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1397 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 1398 start_networkd()
bee692fd 1399
95e1fbba 1400 self.wait_online('test1:degraded', 'veth99:degraded', 'veth-peer:degraded')
bee692fd 1401
10d670a3 1402 networkctl('delete', 'test1', 'veth99')
a962d857
YW
1403 self.check_link_exists('test1', expected=False)
1404 self.check_link_exists('veth99', expected=False)
1405 self.check_link_exists('veth-peer', expected=False)
bee692fd 1406
7e107bc3 1407 def test_label(self):
10d670a3 1408 networkctl('label')
7e107bc3 1409
fa4d3fed
YW
1410class NetworkdMatchTests(unittest.TestCase, Utilities):
1411
1412 def setUp(self):
1413 setup_common()
1414
1415 def tearDown(self):
1416 tear_down_common()
1417
7618ab1b 1418 @expectedFailureIfAlternativeNameIsNotAvailable()
fa4d3fed
YW
1419 def test_match(self):
1420 copy_network_unit('12-dummy-mac.netdev',
1421 '12-dummy-match-mac-01.network',
1422 '12-dummy-match-mac-02.network',
1423 '12-dummy-match-renamed.network',
1424 '12-dummy-match-altname.network',
1425 '12-dummy-altname.link')
1426 start_networkd()
1427
95e1fbba 1428 self.wait_online('dummy98:routable')
10d670a3 1429 output = networkctl_status('dummy98')
fa4d3fed
YW
1430 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
1431 output = check_output('ip -4 address show dev dummy98')
1432 self.assertIn('10.0.0.1/16', output)
1433
1434 check_output('ip link set dev dummy98 down')
1435 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1436
1437 self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1438 self.wait_online('dummy98:routable')
10d670a3 1439 output = networkctl_status('dummy98')
fa4d3fed
YW
1440 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
1441
1442 check_output('ip link set dev dummy98 down')
1443 check_output('ip link set dev dummy98 name dummy98-1')
1444
1445 self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1446 self.wait_online('dummy98-1:routable')
10d670a3 1447 output = networkctl_status('dummy98-1')
fa4d3fed
YW
1448 self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
1449
1450 check_output('ip link set dev dummy98-1 down')
1451 check_output('ip link set dev dummy98-1 name dummy98-2')
a39a2a81 1452 udevadm_trigger('/sys/class/net/dummy98-2')
fa4d3fed
YW
1453
1454 self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1455 self.wait_online('dummy98-2:routable')
10d670a3 1456 output = networkctl_status('dummy98-2')
fa4d3fed
YW
1457 self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
1458
5432adae
YW
1459 def test_match_udev_property(self):
1460 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1461 start_networkd()
95e1fbba 1462 self.wait_online('dummy98:routable')
5432adae 1463
10d670a3 1464 output = networkctl_status('dummy98')
5432adae
YW
1465 print(output)
1466 self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
1467
b09ec847
YW
1468class WaitOnlineTests(unittest.TestCase, Utilities):
1469
1470 def setUp(self):
1471 setup_common()
1472
1473 def tearDown(self):
1474 tear_down_common()
1475
1476 def test_wait_online_any(self):
1477 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1478 start_networkd()
1479
95e1fbba 1480 self.wait_online('bridge99', 'test1:degraded', bool_any=True)
b09ec847
YW
1481
1482 self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
1483 self.wait_operstate('test1', 'degraded')
1484
1f0e3109
SS
1485class NetworkdNetDevTests(unittest.TestCase, Utilities):
1486
1f0e3109 1487 def setUp(self):
a962d857 1488 setup_common()
1f0e3109
SS
1489
1490 def tearDown(self):
a962d857 1491 tear_down_common()
1f0e3109 1492
1ca44d7d 1493 def test_dropin_and_name_conflict(self):
a962d857 1494 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
2cf6fdff 1495 start_networkd()
d80734f7 1496
95e1fbba 1497 self.wait_online('dropin-test:off', setup_state='unmanaged')
d80734f7 1498
371810d1 1499 output = check_output('ip link show dropin-test')
d80734f7 1500 print(output)
45aa0e84 1501 self.assertRegex(output, '00:50:56:c0:00:28')
d80734f7 1502
13060471
YW
1503 @expectedFailureIfModuleIsNotAvailable('bareudp')
1504 def test_bareudp(self):
a962d857 1505 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
13060471
YW
1506 start_networkd()
1507
95e1fbba 1508 self.wait_online('bareudp99:degraded')
13060471
YW
1509
1510 output = check_output('ip -d link show bareudp99')
1511 print(output)
1512 self.assertRegex(output, 'dstport 1000 ')
1513 self.assertRegex(output, 'ethertype ip ')
1514
c0267a59
AW
1515 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1516 def test_batadv(self):
a962d857 1517 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
c0267a59
AW
1518 start_networkd()
1519
95e1fbba 1520 self.wait_online('batadv99:degraded')
c0267a59
AW
1521
1522 output = check_output('ip -d link show batadv99')
1523 print(output)
1524 self.assertRegex(output, 'batadv')
1525
1f0e3109 1526 def test_bridge(self):
a962d857 1527 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
2cf6fdff 1528 start_networkd()
1f0e3109 1529
95e1fbba 1530 self.wait_online('bridge99:no-carrier')
1f0e3109 1531
3d165124 1532 tick = os.sysconf('SC_CLK_TCK')
ec38833c
ZJS
1533 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
1534 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
c9047092
YW
1535 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick))
1536 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick))
1537 self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1538 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1539 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1540 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1541 self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1f0e3109 1542
10d670a3 1543 output = networkctl_status('bridge99')
36bc2ffb
YW
1544 print(output)
1545 self.assertRegex(output, 'Priority: 9')
1546 self.assertRegex(output, 'STP: yes')
1547 self.assertRegex(output, 'Multicast IGMP Version: 3')
1548
b6d5dab7
YW
1549 output = check_output('ip -d link show bridge99')
1550 print(output)
1551 self.assertIn('vlan_filtering 1 ', output)
1552 self.assertIn('vlan_protocol 802.1ad ', output)
1553 self.assertIn('vlan_default_pvid 9 ', output)
1554
1f0e3109 1555 def test_bond(self):
d2d0a8d4 1556 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
2cf6fdff 1557 start_networkd()
ec38833c 1558
d2d0a8d4 1559 self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged')
ec38833c 1560
a962d857
YW
1561 self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1562 self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1563 self.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1564 self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1565 self.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1566 self.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1567 self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1568 self.check_link_attr('bond99', 'bonding', 'min_links', '1')
1569 self.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1570 self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1571 self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1572
1573 self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1574 self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
fde60a42 1575
10d670a3 1576 output = networkctl_status('bond99')
acfe3b65
YW
1577 print(output)
1578 self.assertIn('Mode: 802.3ad', output)
1579 self.assertIn('Miimon: 1s', output)
1580 self.assertIn('Updelay: 2s', output)
1581 self.assertIn('Downdelay: 2s', output)
1582
10d670a3 1583 output = networkctl_status('bond98')
acfe3b65
YW
1584 print(output)
1585 self.assertIn('Mode: balance-tlb', output)
1586
d2d0a8d4
SS
1587 output = networkctl_status('bond97')
1588 print(output)
1589
1590 self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
de736b96 1591 self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
d2d0a8d4 1592
1f0e3109 1593 def test_vlan(self):
a962d857
YW
1594 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1595 '21-vlan.network', '21-vlan-test1.network')
2cf6fdff 1596 start_networkd()
1f0e3109 1597
95e1fbba 1598 self.wait_online('test1:degraded', 'vlan99:routable')
1f0e3109 1599
371810d1 1600 output = check_output('ip -d link show test1')
72b7f1b9 1601 print(output)
7d7be1b9 1602 self.assertRegex(output, ' mtu 2000 ')
72b7f1b9 1603
371810d1 1604 output = check_output('ip -d link show vlan99')
14ecd604 1605 print(output)
73d24e45
YW
1606 self.assertIn(' mtu 2000 ', output)
1607 self.assertIn('REORDER_HDR', output)
1608 self.assertIn('LOOSE_BINDING', output)
1609 self.assertIn('GVRP', output)
1610 self.assertIn('MVRP', output)
1611 self.assertIn(' id 99 ', output)
1612 self.assertIn('ingress-qos-map { 4:100 7:13 }', output)
1613 self.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output)
1f0e3109 1614
371810d1 1615 output = check_output('ip -4 address show dev test1')
7f45d738
YW
1616 print(output)
1617 self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1618 self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1619
371810d1 1620 output = check_output('ip -4 address show dev vlan99')
7f45d738
YW
1621 print(output)
1622 self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1623
b249834b
YW
1624 def test_vlan_on_bond(self):
1625 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1626 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1627
1628 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1629 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1630 start_networkd()
95e1fbba 1631 self.wait_online('bond99:off')
b249834b
YW
1632 self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
1633
45c2bbba 1634 self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
b249834b
YW
1635
1636 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1637 networkctl_reload()
95e1fbba 1638 self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
b249834b 1639
1f0e3109 1640 def test_macvtap(self):
a962d857 1641 first = True
460feb61 1642 for mode in ['private', 'vepa', 'bridge', 'passthru']:
a962d857
YW
1643 if first:
1644 first = False
1645 else:
1646 self.tearDown()
1647
1648 print(f'### test_macvtap(mode={mode})')
460feb61 1649 with self.subTest(mode=mode):
a962d857
YW
1650 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1651 '11-dummy.netdev', '25-macvtap.network')
1652 with open(os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8') as f:
460feb61 1653 f.write('[MACVTAP]\nMode=' + mode)
2cf6fdff 1654 start_networkd()
1f0e3109 1655
95e1fbba
YW
1656 self.wait_online('macvtap99:degraded',
1657 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
460feb61 1658
371810d1 1659 output = check_output('ip -d link show macvtap99')
460feb61
YW
1660 print(output)
1661 self.assertRegex(output, 'macvtap mode ' + mode + ' ')
1f0e3109
SS
1662
1663 def test_macvlan(self):
a962d857 1664 first = True
dff9792b 1665 for mode in ['private', 'vepa', 'bridge', 'passthru']:
a962d857
YW
1666 if first:
1667 first = False
1668 else:
1669 self.tearDown()
1670
1671 print(f'### test_macvlan(mode={mode})')
dff9792b 1672 with self.subTest(mode=mode):
a962d857
YW
1673 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1674 '11-dummy.netdev', '25-macvlan.network')
1675 with open(os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8') as f:
dff9792b 1676 f.write('[MACVLAN]\nMode=' + mode)
2cf6fdff 1677 start_networkd()
dff9792b 1678
95e1fbba
YW
1679 self.wait_online('macvlan99:degraded',
1680 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
dff9792b 1681
371810d1 1682 output = check_output('ip -d link show test1')
dff9792b 1683 print(output)
0183d48d 1684 self.assertIn(' mtu 2000 ', output)
72b7f1b9 1685
371810d1 1686 output = check_output('ip -d link show macvlan99')
dff9792b 1687 print(output)
0183d48d
YW
1688 self.assertIn(' mtu 2000 ', output)
1689 self.assertIn(f' macvlan mode {mode} ', output)
72b7f1b9 1690
a962d857 1691 remove_link('test1')
1d0c9bd7
YW
1692 time.sleep(1)
1693
a962d857 1694 check_output("ip link add test1 type dummy")
95e1fbba
YW
1695 self.wait_online('macvlan99:degraded',
1696 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1d0c9bd7
YW
1697
1698 output = check_output('ip -d link show test1')
1699 print(output)
0183d48d 1700 self.assertIn(' mtu 2000 ', output)
1d0c9bd7
YW
1701
1702 output = check_output('ip -d link show macvlan99')
1703 print(output)
0183d48d
YW
1704 self.assertIn(' mtu 2000 ', output)
1705 self.assertIn(f' macvlan mode {mode} ', output)
2b98febe
SS
1706 self.assertIn(' bcqueuelen 1234 ', output)
1707 self.assertIn(' bclim 2147483647 ', output)
1d0c9bd7 1708
7a0a37b2 1709 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1f0e3109 1710 def test_ipvlan(self):
a962d857 1711 first = True
bc6dff6e 1712 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
a962d857
YW
1713 if first:
1714 first = False
1715 else:
1716 self.tearDown()
1717
1718 print(f'### test_ipvlan(mode={mode}, flag={flag})')
bc6dff6e 1719 with self.subTest(mode=mode, flag=flag):
a962d857
YW
1720 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1721 '11-dummy.netdev', '25-ipvlan.network')
1722 with open(os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8') as f:
bc6dff6e 1723 f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
1f0e3109 1724
2cf6fdff 1725 start_networkd()
95e1fbba 1726 self.wait_online('ipvlan99:degraded', 'test1:degraded')
bc6dff6e 1727
371810d1 1728 output = check_output('ip -d link show ipvlan99')
bc6dff6e
YW
1729 print(output)
1730 self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
1f0e3109 1731
956c8fec
YW
1732 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1733 def test_ipvtap(self):
a962d857 1734 first = True
40921f08 1735 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
a962d857
YW
1736 if first:
1737 first = False
1738 else:
1739 self.tearDown()
1740
1741 print(f'### test_ipvtap(mode={mode}, flag={flag})')
40921f08 1742 with self.subTest(mode=mode, flag=flag):
a962d857
YW
1743 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1744 '11-dummy.netdev', '25-ipvtap.network')
1745 with open(os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8') as f:
40921f08
YW
1746 f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
1747
2cf6fdff 1748 start_networkd()
95e1fbba 1749 self.wait_online('ipvtap99:degraded', 'test1:degraded')
956c8fec 1750
371810d1 1751 output = check_output('ip -d link show ipvtap99')
40921f08
YW
1752 print(output)
1753 self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
956c8fec 1754
1f0e3109 1755 def test_veth(self):
a962d857
YW
1756 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1757 '25-veth-mtu.netdev')
2cf6fdff 1758 start_networkd()
1f0e3109 1759
95e1fbba 1760 self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
671dacdf 1761
371810d1 1762 output = check_output('ip -d link show veth99')
671dacdf
YW
1763 print(output)
1764 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
371810d1 1765 output = check_output('ip -d link show veth-peer')
671dacdf
YW
1766 print(output)
1767 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
1f0e3109 1768
0874be35
YW
1769 output = check_output('ip -d link show veth-mtu')
1770 print(output)
1771 self.assertRegex(output, 'link/ether 12:34:56:78:9a:be')
1772 self.assertRegex(output, 'mtu 1800')
1773 output = check_output('ip -d link show veth-mtu-peer')
1774 print(output)
1775 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
1776 self.assertRegex(output, 'mtu 1800')
1777
5cdc7c89 1778 def test_tuntap(self):
ae014ecb 1779 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 1780 start_networkd()
1f0e3109 1781
95e1fbba 1782 self.wait_online('testtun99:degraded', 'testtap99:degraded')
ae014ecb
YW
1783
1784 pid = networkd_pid()
1785 name = psutil.Process(pid).name()[:15]
1786
1787 output = check_output('ip -d tuntap show')
1788 print(output)
0677130e
FS
1789 self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1790 self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1f0e3109 1791
5cdc7c89 1792 output = check_output('ip -d link show testtun99')
2746d307
YW
1793 print(output)
1794 # Old ip command does not support IFF_ flags
426654d7 1795 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
ae014ecb 1796 self.assertIn('UP,LOWER_UP', output)
2746d307 1797
5cdc7c89 1798 output = check_output('ip -d link show testtap99')
2746d307 1799 print(output)
426654d7 1800 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
ae014ecb
YW
1801 self.assertIn('UP,LOWER_UP', output)
1802
1803 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1804
1805 restart_networkd()
95e1fbba 1806 self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged')
ae014ecb
YW
1807
1808 pid = networkd_pid()
1809 name = psutil.Process(pid).name()[:15]
1810
1811 output = check_output('ip -d tuntap show')
1812 print(output)
0677130e
FS
1813 self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1814 self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
ae014ecb
YW
1815
1816 output = check_output('ip -d link show testtun99')
1817 print(output)
1818 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1819 self.assertIn('UP,LOWER_UP', output)
1820
1821 output = check_output('ip -d link show testtap99')
1822 print(output)
1823 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1824 self.assertIn('UP,LOWER_UP', output)
1825
1826 clear_network_units()
1827 restart_networkd()
95e1fbba 1828 self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged')
ae014ecb
YW
1829
1830 output = check_output('ip -d tuntap show')
1831 print(output)
0677130e
FS
1832 self.assertRegex(output, r'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1833 self.assertRegex(output, r'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
ae014ecb
YW
1834
1835 for i in range(10):
1836 if i != 0:
1837 time.sleep(1)
1838 output = check_output('ip -d link show testtun99')
1839 print(output)
1840 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1841 if 'NO-CARRIER' in output:
1842 break
1843 else:
1844 self.fail()
1845
1846 for i in range(10):
1847 if i != 0:
1848 time.sleep(1)
1849 output = check_output('ip -d link show testtap99')
1850 print(output)
1851 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1852 if 'NO-CARRIER' in output:
1853 break
1854 else:
1855 self.fail()
2746d307 1856
7a0a37b2 1857 @expectedFailureIfModuleIsNotAvailable('vrf')
1f0e3109 1858 def test_vrf(self):
a962d857 1859 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 1860 start_networkd()
1f0e3109 1861
95e1fbba 1862 self.wait_online('vrf99:carrier')
1f0e3109 1863
7a0a37b2 1864 @expectedFailureIfModuleIsNotAvailable('vcan')
1f0e3109 1865 def test_vcan(self):
470a329d
YW
1866 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1867 '25-vcan98.netdev', '25-vcan98.network')
2cf6fdff 1868 start_networkd()
1f0e3109 1869
95e1fbba 1870 self.wait_online('vcan99:carrier', 'vcan98:carrier')
7155ad95 1871 # For can devices, 'carrier' is the default required operational state.
95e1fbba 1872 self.wait_online('vcan99', 'vcan98')
470a329d
YW
1873
1874 # https://github.com/systemd/systemd/issues/30140
1875 output = check_output('ip -d link show vcan99')
1876 print(output)
1877 self.assertIn('mtu 16 ', output)
1878
1879 output = check_output('ip -d link show vcan98')
1880 print(output)
1881 self.assertIn('mtu 16 ', output)
1f0e3109 1882
f63b14d3
YW
1883 @expectedFailureIfModuleIsNotAvailable('vxcan')
1884 def test_vxcan(self):
a962d857 1885 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 1886 start_networkd()
f63b14d3 1887
95e1fbba 1888 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
7155ad95 1889 # For can devices, 'carrier' is the default required operational state.
95e1fbba 1890 self.wait_online('vxcan99', 'vxcan-peer')
f63b14d3 1891
7a3bc5a8
EV
1892 @expectedFailureIfModuleIsNotAvailable('wireguard')
1893 def test_wireguard(self):
fa724cd5
MY
1894 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1895 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1896 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1897
a962d857
YW
1898 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1899 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1900 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1901 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2cf6fdff 1902 start_networkd()
95e1fbba 1903 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
84d32bf5
YW
1904
1905 output = check_output('ip -4 address show dev wg99')
1906 print(output)
1907 self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
5a0bd90b 1908
e4e0b239
YW
1909 output = check_output('ip -4 address show dev wg99')
1910 print(output)
1911 self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
1912
1913 output = check_output('ip -6 address show dev wg99')
1914 print(output)
1915 self.assertIn('inet6 fe80::1/64 scope link', output)
1916
045db4fa
YW
1917 output = check_output('ip -4 address show dev wg98')
1918 print(output)
1919 self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
1920
1921 output = check_output('ip -6 address show dev wg98')
1922 print(output)
1923 self.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output)
1924
6387cac3
YW
1925 output = check_output('ip -4 route show dev wg99 table 1234')
1926 print(output)
4db8ccbb 1927 self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
6387cac3
YW
1928
1929 output = check_output('ip -6 route show dev wg99 table 1234')
1930 print(output)
1931 self.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output)
1932
1933 output = check_output('ip -6 route show dev wg98 table 1234')
1934 print(output)
1935 self.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output)
1936 self.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output)
1937 self.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output)
1938 self.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output)
1939 self.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output)
1940 self.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output)
1941 self.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output)
1942 self.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output)
1943 self.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output)
1944 self.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output)
1945 self.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output)
1946 self.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output)
1947 self.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output)
1948 self.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output)
1949 self.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output)
1950 self.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output)
1951 self.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output)
1952 self.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output)
1953 self.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output)
1954 self.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output)
1955 self.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output)
1956 self.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output)
1957 self.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output)
1958 self.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output)
1959 self.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output)
1960 self.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output)
1961 self.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output)
1962 self.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output)
1963 self.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output)
1964 self.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output)
1965 self.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output)
1966 self.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output)
1967 self.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output)
1968 self.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output)
1969 self.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output)
1970 self.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output)
1971 self.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output)
1972 self.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output)
1973 self.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output)
1974 self.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output)
1975 self.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output)
1976 self.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output)
1977 self.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output)
1978 self.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output)
1979 self.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output)
1980 self.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output)
1981
7a3bc5a8 1982 if shutil.which('wg'):
371810d1 1983 call('wg')
5a0bd90b 1984
371810d1 1985 output = check_output('wg show wg99 listen-port')
84d32bf5 1986 self.assertEqual(output, '51820')
371810d1 1987 output = check_output('wg show wg99 fwmark')
84d32bf5
YW
1988 self.assertEqual(output, '0x4d2')
1989 output = check_output('wg show wg99 private-key')
1990 self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
371810d1 1991 output = check_output('wg show wg99 allowed-ips')
84d32bf5
YW
1992 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
1993 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
1994 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
1995 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
371810d1 1996 output = check_output('wg show wg99 persistent-keepalive')
84d32bf5
YW
1997 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
1998 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
1999 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
2000 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
371810d1 2001 output = check_output('wg show wg99 endpoints')
84d32bf5
YW
2002 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
2003 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
2004 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
2005 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
371810d1 2006 output = check_output('wg show wg99 preshared-keys')
84d32bf5
YW
2007 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
2008 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
2009 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
2010 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
7a3bc5a8 2011
371810d1 2012 output = check_output('wg show wg98 private-key')
84d32bf5 2013 self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
da44fb8a 2014
da3509f0 2015 output = check_output('wg show wg97 listen-port')
84d32bf5 2016 self.assertEqual(output, '51821')
da3509f0 2017 output = check_output('wg show wg97 fwmark')
84d32bf5 2018 self.assertEqual(output, '0x4d3')
da3509f0 2019
1f0e3109 2020 def test_geneve(self):
a962d857 2021 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2022 start_networkd()
1f0e3109 2023
95e1fbba 2024 self.wait_online('geneve99:degraded')
1f0e3109 2025
371810d1 2026 output = check_output('ip -d link show geneve99')
14ecd604 2027 print(output)
06895a1d
YW
2028 self.assertRegex(output, '192.168.22.1')
2029 self.assertRegex(output, '6082')
2030 self.assertRegex(output, 'udpcsum')
2031 self.assertRegex(output, 'udp6zerocsumrx')
1f0e3109
SS
2032
2033 def test_ipip_tunnel(self):
a962d857
YW
2034 copy_network_unit('12-dummy.netdev', '25-ipip.network',
2035 '25-ipip-tunnel.netdev', '25-tunnel.network',
2036 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2037 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2038 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2039 start_networkd()
95e1fbba 2040 self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
6a97a864 2041
371810d1 2042 output = check_output('ip -d link show ipiptun99')
6a97a864 2043 print(output)
426654d7 2044 self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
371810d1 2045 output = check_output('ip -d link show ipiptun98')
6a97a864 2046 print(output)
426654d7 2047 self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
371810d1 2048 output = check_output('ip -d link show ipiptun97')
6a97a864 2049 print(output)
426654d7 2050 self.assertRegex(output, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
42a29fcb
YW
2051 output = check_output('ip -d link show ipiptun96')
2052 print(output)
426654d7 2053 self.assertRegex(output, 'ipip (ipip )?remote any local any dev dummy98')
1f0e3109
SS
2054
2055 def test_gre_tunnel(self):
a962d857
YW
2056 copy_network_unit('12-dummy.netdev', '25-gretun.network',
2057 '25-gre-tunnel.netdev', '25-tunnel.network',
2058 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2059 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2060 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2061 start_networkd()
95e1fbba 2062 self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
6a97a864 2063
371810d1 2064 output = check_output('ip -d link show gretun99')
6a97a864
YW
2065 print(output)
2066 self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
38f4bb44
YW
2067 self.assertRegex(output, 'ikey 1.2.3.103')
2068 self.assertRegex(output, 'okey 1.2.4.103')
2069 self.assertRegex(output, 'iseq')
2070 self.assertRegex(output, 'oseq')
371810d1 2071 output = check_output('ip -d link show gretun98')
6a97a864
YW
2072 print(output)
2073 self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
38f4bb44
YW
2074 self.assertRegex(output, 'ikey 0.0.0.104')
2075 self.assertRegex(output, 'okey 0.0.0.104')
2076 self.assertNotRegex(output, 'iseq')
2077 self.assertNotRegex(output, 'oseq')
371810d1 2078 output = check_output('ip -d link show gretun97')
6a97a864
YW
2079 print(output)
2080 self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
38f4bb44
YW
2081 self.assertRegex(output, 'ikey 0.0.0.105')
2082 self.assertRegex(output, 'okey 0.0.0.105')
2083 self.assertNotRegex(output, 'iseq')
2084 self.assertNotRegex(output, 'oseq')
42a29fcb
YW
2085 output = check_output('ip -d link show gretun96')
2086 print(output)
2087 self.assertRegex(output, 'gre remote any local any dev dummy98')
2088 self.assertRegex(output, 'ikey 0.0.0.106')
2089 self.assertRegex(output, 'okey 0.0.0.106')
2090 self.assertNotRegex(output, 'iseq')
2091 self.assertNotRegex(output, 'oseq')
6a97a864
YW
2092
2093 def test_ip6gre_tunnel(self):
a962d857
YW
2094 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2095 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2096 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2097 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2098 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2099 start_networkd()
6a97a864 2100
17bcf0a0
YW
2101 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2102
a962d857 2103 self.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
6a97a864 2104
371810d1 2105 output = check_output('ip -d link show ip6gretun99')
6a97a864
YW
2106 print(output)
2107 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2108 output = check_output('ip -d link show ip6gretun98')
6a97a864
YW
2109 print(output)
2110 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
371810d1 2111 output = check_output('ip -d link show ip6gretun97')
6a97a864
YW
2112 print(output)
2113 self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
42a29fcb
YW
2114 output = check_output('ip -d link show ip6gretun96')
2115 print(output)
2116 self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
1f0e3109 2117
11309591 2118 def test_gretap_tunnel(self):
a962d857
YW
2119 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2120 '25-gretap-tunnel.netdev', '25-tunnel.network',
2121 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2122 start_networkd()
95e1fbba 2123 self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
6a97a864 2124
371810d1 2125 output = check_output('ip -d link show gretap99')
6a97a864
YW
2126 print(output)
2127 self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
38f4bb44
YW
2128 self.assertRegex(output, 'ikey 0.0.0.106')
2129 self.assertRegex(output, 'okey 0.0.0.106')
2130 self.assertRegex(output, 'iseq')
2131 self.assertRegex(output, 'oseq')
b67e8a4e
YZ
2132 self.assertIn('nopmtudisc', output)
2133 self.assertIn('ignore-df', output)
371810d1 2134 output = check_output('ip -d link show gretap98')
6a97a864
YW
2135 print(output)
2136 self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
38f4bb44
YW
2137 self.assertRegex(output, 'ikey 0.0.0.107')
2138 self.assertRegex(output, 'okey 0.0.0.107')
2139 self.assertRegex(output, 'iseq')
2140 self.assertRegex(output, 'oseq')
1f0e3109
SS
2141
2142 def test_ip6gretap_tunnel(self):
a962d857
YW
2143 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2144 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2145 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2146 start_networkd()
95e1fbba 2147 self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
6a97a864 2148
371810d1 2149 output = check_output('ip -d link show ip6gretap99')
6a97a864
YW
2150 print(output)
2151 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2152 output = check_output('ip -d link show ip6gretap98')
6a97a864
YW
2153 print(output)
2154 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
1f0e3109
SS
2155
2156 def test_vti_tunnel(self):
a962d857
YW
2157 copy_network_unit('12-dummy.netdev', '25-vti.network',
2158 '25-vti-tunnel.netdev', '25-tunnel.network',
2159 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2160 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2161 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2162 start_networkd()
95e1fbba 2163 self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
6a97a864 2164
371810d1 2165 output = check_output('ip -d link show vtitun99')
6a97a864
YW
2166 print(output)
2167 self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
371810d1 2168 output = check_output('ip -d link show vtitun98')
6a97a864
YW
2169 print(output)
2170 self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
371810d1 2171 output = check_output('ip -d link show vtitun97')
6a97a864
YW
2172 print(output)
2173 self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
42a29fcb
YW
2174 output = check_output('ip -d link show vtitun96')
2175 print(output)
2176 self.assertRegex(output, 'vti remote any local any dev dummy98')
1f0e3109
SS
2177
2178 def test_vti6_tunnel(self):
a962d857
YW
2179 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2180 '25-vti6-tunnel.netdev', '25-tunnel.network',
2181 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2182 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2cf6fdff 2183 start_networkd()
95e1fbba 2184 self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
6a97a864 2185
371810d1 2186 output = check_output('ip -d link show vti6tun99')
6a97a864
YW
2187 print(output)
2188 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2189 output = check_output('ip -d link show vti6tun98')
6a97a864 2190 print(output)
426654d7 2191 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
371810d1 2192 output = check_output('ip -d link show vti6tun97')
6a97a864 2193 print(output)
426654d7 2194 self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1f0e3109
SS
2195
2196 def test_ip6tnl_tunnel(self):
a962d857
YW
2197 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2198 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2199 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2200 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2201 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2202 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2203 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2204 start_networkd()
95e1fbba
YW
2205 self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2206 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2207 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
6a97a864 2208
371810d1 2209 output = check_output('ip -d link show ip6tnl99')
6a97a864 2210 print(output)
da7d6848 2211 self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
371810d1 2212 output = check_output('ip -d link show ip6tnl98')
6a97a864 2213 print(output)
426654d7 2214 self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
371810d1 2215 output = check_output('ip -d link show ip6tnl97')
6a97a864 2216 print(output)
426654d7 2217 self.assertRegex(output, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
7809cab7
YW
2218 output = check_output('ip -d link show ip6tnl-external')
2219 print(output)
2220 self.assertIn('ip6tnl-external@NONE:', output)
2221 self.assertIn('ip6tnl external ', output)
da7d6848
YW
2222 output = check_output('ip -d link show ip6tnl-slaac')
2223 print(output)
2224 self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
2225
2226 output = check_output('ip -6 address show veth99')
2227 print(output)
2228 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
2229
2230 output = check_output('ip -4 route show default')
2231 print(output)
2232 self.assertIn('default dev ip6tnl-slaac proto static', output)
1f0e3109
SS
2233
2234 def test_sit_tunnel(self):
a962d857
YW
2235 copy_network_unit('12-dummy.netdev', '25-sit.network',
2236 '25-sit-tunnel.netdev', '25-tunnel.network',
2237 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2238 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2239 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2240 start_networkd()
95e1fbba 2241 self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
6a97a864 2242
371810d1 2243 output = check_output('ip -d link show sittun99')
6a97a864 2244 print(output)
426654d7 2245 self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
371810d1 2246 output = check_output('ip -d link show sittun98')
6a97a864 2247 print(output)
426654d7 2248 self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
371810d1 2249 output = check_output('ip -d link show sittun97')
6a97a864 2250 print(output)
426654d7 2251 self.assertRegex(output, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
42a29fcb
YW
2252 output = check_output('ip -d link show sittun96')
2253 print(output)
426654d7 2254 self.assertRegex(output, "sit (ip6ip )?remote any local any dev dummy98")
1f0e3109 2255
d0e728b6 2256 def test_isatap_tunnel(self):
a962d857
YW
2257 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2258 '25-isatap-tunnel.netdev', '25-tunnel.network')
2cf6fdff 2259 start_networkd()
95e1fbba 2260 self.wait_online('isataptun99:routable', 'dummy98:degraded')
d0e728b6 2261
371810d1 2262 output = check_output('ip -d link show isataptun99')
14ecd604 2263 print(output)
d0e728b6
SS
2264 self.assertRegex(output, "isatap ")
2265
d29dc4f1 2266 def test_6rd_tunnel(self):
a962d857
YW
2267 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2268 '25-6rd-tunnel.netdev', '25-tunnel.network')
2cf6fdff 2269 start_networkd()
95e1fbba 2270 self.wait_online('sittun99:routable', 'dummy98:degraded')
d29dc4f1 2271
371810d1 2272 output = check_output('ip -d link show sittun99')
6a97a864
YW
2273 print(output)
2274 self.assertRegex(output, '6rd-prefix 2602::/24')
2275
2f0260c1
YW
2276 @expectedFailureIfERSPANv0IsNotSupported()
2277 def test_erspan_tunnel_v0(self):
a962d857
YW
2278 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2279 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2280 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2281 start_networkd()
95e1fbba 2282 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2266864b 2283
371810d1 2284 output = check_output('ip -d link show erspan99')
6a97a864 2285 print(output)
2f0260c1
YW
2286 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2287 self.assertIn('erspan_ver 0', output)
2288 self.assertNotIn('erspan_index 123', output)
2289 self.assertNotIn('erspan_dir ingress', output)
2290 self.assertNotIn('erspan_hwid 1f', output)
2291 self.assertIn('ikey 0.0.0.101', output)
2292 self.assertIn('iseq', output)
b67e8a4e
YZ
2293 self.assertIn('nopmtudisc', output)
2294 self.assertIn('ignore-df', output)
371810d1 2295 output = check_output('ip -d link show erspan98')
2266864b 2296 print(output)
2f0260c1
YW
2297 self.assertIn('erspan remote 172.16.1.100 local any', output)
2298 self.assertIn('erspan_ver 0', output)
2299 self.assertNotIn('erspan_index 124', output)
2300 self.assertNotIn('erspan_dir egress', output)
2301 self.assertNotIn('erspan_hwid 2f', output)
2302 self.assertIn('ikey 0.0.0.102', output)
2303 self.assertIn('iseq', output)
2304
2305 def test_erspan_tunnel_v1(self):
a962d857
YW
2306 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2307 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2308 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2f0260c1 2309 start_networkd()
95e1fbba 2310 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2f0260c1
YW
2311
2312 output = check_output('ip -d link show erspan99')
2313 print(output)
2314 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2315 self.assertIn('erspan_ver 1', output)
2316 self.assertIn('erspan_index 123', output)
2317 self.assertNotIn('erspan_dir ingress', output)
2318 self.assertNotIn('erspan_hwid 1f', output)
2319 self.assertIn('ikey 0.0.0.101', output)
2320 self.assertIn('okey 0.0.0.101', output)
2321 self.assertIn('iseq', output)
2322 self.assertIn('oseq', output)
2323 output = check_output('ip -d link show erspan98')
2324 print(output)
2325 self.assertIn('erspan remote 172.16.1.100 local any', output)
2326 self.assertIn('erspan_ver 1', output)
2327 self.assertIn('erspan_index 124', output)
2328 self.assertNotIn('erspan_dir egress', output)
2329 self.assertNotIn('erspan_hwid 2f', output)
2330 self.assertIn('ikey 0.0.0.102', output)
2331 self.assertIn('okey 0.0.0.102', output)
2332 self.assertIn('iseq', output)
2333 self.assertIn('oseq', output)
2334
2335 @expectedFailureIfERSPANv2IsNotSupported()
2336 def test_erspan_tunnel_v2(self):
a962d857
YW
2337 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2338 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2339 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2f0260c1 2340 start_networkd()
95e1fbba 2341 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2f0260c1
YW
2342
2343 output = check_output('ip -d link show erspan99')
2344 print(output)
2345 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2346 self.assertIn('erspan_ver 2', output)
2347 self.assertNotIn('erspan_index 123', output)
2348 self.assertIn('erspan_dir ingress', output)
2349 self.assertIn('erspan_hwid 0x1f', output)
2350 self.assertIn('ikey 0.0.0.101', output)
2351 self.assertIn('okey 0.0.0.101', output)
2352 self.assertIn('iseq', output)
2353 self.assertIn('oseq', output)
2354 output = check_output('ip -d link show erspan98')
2355 print(output)
2356 self.assertIn('erspan remote 172.16.1.100 local any', output)
2357 self.assertIn('erspan_ver 2', output)
2358 self.assertNotIn('erspan_index 124', output)
2359 self.assertIn('erspan_dir egress', output)
2360 self.assertIn('erspan_hwid 0x2f', output)
2361 self.assertIn('ikey 0.0.0.102', output)
2362 self.assertIn('okey 0.0.0.102', output)
2363 self.assertIn('iseq', output)
2364 self.assertIn('oseq', output)
2266864b 2365
1f0e3109 2366 def test_tunnel_independent(self):
a962d857 2367 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2368 start_networkd()
e40a58b5 2369
95e1fbba 2370 self.wait_online('ipiptun99:carrier')
1f0e3109 2371
95082dbe 2372 def test_tunnel_independent_loopback(self):
a962d857 2373 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
95082dbe
YW
2374 start_networkd()
2375
95e1fbba 2376 self.wait_online('ipiptun99:carrier')
95082dbe 2377
e64dc406
YW
2378 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2379 def test_xfrm(self):
a962d857
YW
2380 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2381 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2382 '26-netdev-link-local-addressing-yes.network')
e64dc406
YW
2383 start_networkd()
2384
95e1fbba 2385 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
e64dc406 2386
020483b2 2387 output = check_output('ip -d link show dev xfrm98')
e64dc406 2388 print(output)
020483b2
YW
2389 self.assertIn('xfrm98@dummy98:', output)
2390 self.assertIn('xfrm if_id 0x98 ', output)
e64dc406 2391
020483b2
YW
2392 output = check_output('ip -d link show dev xfrm99')
2393 print(output)
2394 self.assertIn('xfrm99@lo:', output)
2395 self.assertIn('xfrm if_id 0x99 ', output)
e64dc406 2396
4b6a6d1e
YW
2397 @expectedFailureIfModuleIsNotAvailable('fou')
2398 def test_fou(self):
2399 # The following redundant check is necessary for CentOS CI.
2400 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2401 self.assertTrue(is_module_available('fou'))
2402
a962d857
YW
2403 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2404 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2405 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2cf6fdff 2406 start_networkd()
4b6a6d1e 2407
95e1fbba 2408 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
4b6a6d1e 2409
371810d1 2410 output = check_output('ip fou show')
4b6a6d1e
YW
2411 print(output)
2412 self.assertRegex(output, 'port 55555 ipproto 4')
2413 self.assertRegex(output, 'port 55556 ipproto 47')
2414
371810d1 2415 output = check_output('ip -d link show ipiptun96')
4b6a6d1e
YW
2416 print(output)
2417 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
371810d1 2418 output = check_output('ip -d link show sittun96')
4b6a6d1e
YW
2419 print(output)
2420 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
371810d1 2421 output = check_output('ip -d link show gretun96')
4b6a6d1e
YW
2422 print(output)
2423 self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
371810d1 2424 output = check_output('ip -d link show gretap96')
4b6a6d1e
YW
2425 print(output)
2426 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
2427
1f0e3109 2428 def test_vxlan(self):
a962d857
YW
2429 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2430 '25-vxlan.netdev', '25-vxlan.network',
2431 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2432 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2433 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2434 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2cf6fdff 2435 start_networkd()
1f0e3109 2436
95e1fbba
YW
2437 self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2438 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
1f0e3109 2439
a5e478b2 2440 output = check_output('ip -d -d link show vxlan99')
14ecd604 2441 print(output)
cca07d91
YW
2442 self.assertIn('999', output)
2443 self.assertIn('5555', output)
2444 self.assertIn('l2miss', output)
2445 self.assertIn('l3miss', output)
cca07d91 2446 self.assertIn('gbp', output)
a5e478b2
FS
2447 # Since [0] some of the options use slightly different names and some
2448 # options with default values are shown only if the -d(etails) setting
2449 # is repeated
2450 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2451 self.assertRegex(output, '(udpcsum|udp_csum)')
2452 self.assertRegex(output, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2453 self.assertRegex(output, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2454 self.assertRegex(output, '(remcsumtx|remcsum_tx)')
2455 self.assertRegex(output, '(remcsumrx|remcsum_rx)')
1f0e3109 2456
371810d1 2457 output = check_output('bridge fdb show dev vxlan99')
1c862fe0 2458 print(output)
cca07d91
YW
2459 self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
2460 self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
2461 self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
1c862fe0 2462
10d670a3 2463 output = networkctl_status('vxlan99')
36bc2ffb 2464 print(output)
cca07d91
YW
2465 self.assertIn('VNI: 999', output)
2466 self.assertIn('Destination Port: 5555', output)
2467 self.assertIn('Underlying Device: test1', output)
2468
2469 output = check_output('bridge fdb show dev vxlan97')
2470 print(output)
2471 self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
2472 self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
2473 self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
36bc2ffb 2474
49ad8da7
YW
2475 output = check_output('ip -d link show vxlan-slaac')
2476 print(output)
2477 self.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
2478
2479 output = check_output('ip -6 address show veth99')
2480 print(output)
2481 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
2482
b3de9d7b 2483 @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
02849d8b 2484 def test_macsec(self):
a962d857
YW
2485 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2486 '26-macsec.network', '12-dummy.netdev')
2cf6fdff 2487 start_networkd()
02849d8b 2488
95e1fbba 2489 self.wait_online('dummy98:degraded', 'macsec99:routable')
02849d8b 2490
371810d1 2491 output = check_output('ip -d link show macsec99')
02849d8b
YW
2492 print(output)
2493 self.assertRegex(output, 'macsec99@dummy98')
2494 self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
2495 self.assertRegex(output, 'encrypt on')
2496
371810d1 2497 output = check_output('ip macsec show macsec99')
02849d8b
YW
2498 print(output)
2499 self.assertRegex(output, 'encrypt on')
2500 self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
2501 self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2502 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2503 self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
2504 self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2505 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2506 self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2507 self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2508 self.assertNotRegex(output, 'key 02030405067080900000000000000000')
2509 self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
2510 self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2511
811f33d0 2512 def test_nlmon(self):
a962d857 2513 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2514 start_networkd()
811f33d0 2515
95e1fbba 2516 self.wait_online('nlmon99:carrier')
02849d8b 2517
b076d5d7
YW
2518 @expectedFailureIfModuleIsNotAvailable('ifb')
2519 def test_ifb(self):
a962d857 2520 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
b076d5d7
YW
2521 start_networkd()
2522
95e1fbba 2523 self.wait_online('ifb99:degraded')
b076d5d7 2524
a6f5673c
RRZ
2525 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
2526 def test_rps_cpu_1(self):
4b35dab8 2527 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
a6f5673c
RRZ
2528 start_networkd()
2529
4b35dab8 2530 self.wait_online('dummy98:carrier')
a6f5673c
RRZ
2531
2532 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2533 print(output)
2534 self.assertEqual(int(output.replace(',', ''), base=16), 2)
2535
2536 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
2537 def test_rps_cpu_0_1(self):
4b35dab8 2538 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
a6f5673c
RRZ
2539 start_networkd()
2540
4b35dab8 2541 self.wait_online('dummy98:carrier')
a6f5673c
RRZ
2542
2543 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2544 print(output)
2545 self.assertEqual(int(output.replace(',', ''), base=16), 3)
2546
2547 @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test")
2548 def test_rps_cpu_multi(self):
4b35dab8 2549 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
a6f5673c
RRZ
2550 start_networkd()
2551
4b35dab8 2552 self.wait_online('dummy98:carrier')
a6f5673c
RRZ
2553
2554 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2555 print(output)
2556 self.assertEqual(int(output.replace(',', ''), base=16), 15)
2557
4b35dab8 2558 def test_rps_cpu(self):
a6f5673c
RRZ
2559 cpu_count = os.cpu_count()
2560
4b35dab8 2561 copy_network_unit('12-dummy.netdev', '12-dummy.network')
a6f5673c
RRZ
2562 start_networkd()
2563
4b35dab8 2564 self.wait_online('dummy98:carrier')
a6f5673c 2565
4b35dab8
YW
2566 # 0
2567 copy_network_unit('25-rps-cpu-0.link')
2568 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2569 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2570 print(output)
4b35dab8
YW
2571 self.assertEqual(int(output.replace(',', ''), base=16), 1)
2572 remove_network_unit('25-rps-cpu-0.link')
a6f5673c 2573
4b35dab8
YW
2574 # all
2575 copy_network_unit('25-rps-cpu-all.link')
2576 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2577 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2578 print(output)
4b35dab8
YW
2579 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
2580 remove_network_unit('25-rps-cpu-all.link')
a6f5673c 2581
4b35dab8
YW
2582 # disable
2583 copy_network_unit('24-rps-cpu-disable.link')
2584 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2585 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2586 print(output)
2587 self.assertEqual(int(output.replace(',', ''), base=16), 0)
4b35dab8 2588 remove_network_unit('24-rps-cpu-disable.link')
a6f5673c 2589
4b35dab8
YW
2590 # set all again
2591 copy_network_unit('25-rps-cpu-all.link')
2592 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2593 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2594 print(output)
4b35dab8
YW
2595 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
2596 remove_network_unit('25-rps-cpu-all.link')
a6f5673c 2597
4b35dab8
YW
2598 # empty -> unchanged
2599 copy_network_unit('24-rps-cpu-empty.link')
2600 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2601 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2602 print(output)
4b35dab8
YW
2603 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
2604 remove_network_unit('24-rps-cpu-empty.link')
a6f5673c 2605
4b35dab8
YW
2606 # 0, then empty -> unchanged
2607 copy_network_unit('25-rps-cpu-0-empty.link')
2608 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2609 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2610 print(output)
4b35dab8
YW
2611 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
2612 remove_network_unit('25-rps-cpu-0-empty.link')
a6f5673c 2613
4b35dab8
YW
2614 # 0, then invalid -> 0
2615 copy_network_unit('25-rps-cpu-0-invalid.link')
2616 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2617 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2618 print(output)
2619 self.assertEqual(int(output.replace(',', ''), base=16), 1)
4b35dab8 2620 remove_network_unit('25-rps-cpu-0-invalid.link')
a6f5673c 2621
4b35dab8
YW
2622 # invalid -> unchanged
2623 copy_network_unit('24-rps-cpu-invalid.link')
2624 udevadm_trigger('/sys/class/net/dummy98')
a6f5673c
RRZ
2625 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2626 print(output)
4b35dab8
YW
2627 self.assertEqual(int(output.replace(',', ''), base=16), 1)
2628 remove_network_unit('24-rps-cpu-invalid.link')
a6f5673c 2629
cff83db9
YW
2630class NetworkdL2TPTests(unittest.TestCase, Utilities):
2631
cff83db9 2632 def setUp(self):
a962d857 2633 setup_common()
cff83db9
YW
2634
2635 def tearDown(self):
a962d857 2636 tear_down_common()
cff83db9 2637
59edcf2b 2638 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
cff83db9 2639 def test_l2tp_udp(self):
a962d857
YW
2640 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2641 '25-l2tp-udp.netdev', '25-l2tp.network')
2cf6fdff 2642 start_networkd()
cff83db9 2643
95e1fbba 2644 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
cff83db9 2645
371810d1 2646 output = check_output('ip l2tp show tunnel tunnel_id 10')
cff83db9
YW
2647 print(output)
2648 self.assertRegex(output, "Tunnel 10, encap UDP")
2649 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
2650 self.assertRegex(output, "Peer tunnel 11")
2651 self.assertRegex(output, "UDP source / dest ports: 3000/4000")
2652 self.assertRegex(output, "UDP checksum: enabled")
2653
371810d1 2654 output = check_output('ip l2tp show session tid 10 session_id 15')
cff83db9
YW
2655 print(output)
2656 self.assertRegex(output, "Session 15 in tunnel 10")
2657 self.assertRegex(output, "Peer session 16, tunnel 11")
2658 self.assertRegex(output, "interface name: l2tp-ses1")
2659
371810d1 2660 output = check_output('ip l2tp show session tid 10 session_id 17')
cff83db9
YW
2661 print(output)
2662 self.assertRegex(output, "Session 17 in tunnel 10")
2663 self.assertRegex(output, "Peer session 18, tunnel 11")
2664 self.assertRegex(output, "interface name: l2tp-ses2")
2665
59edcf2b 2666 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
cff83db9 2667 def test_l2tp_ip(self):
a962d857
YW
2668 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2669 '25-l2tp-ip.netdev', '25-l2tp.network')
2cf6fdff 2670 start_networkd()
cff83db9 2671
95e1fbba 2672 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
cff83db9 2673
371810d1 2674 output = check_output('ip l2tp show tunnel tunnel_id 10')
cff83db9
YW
2675 print(output)
2676 self.assertRegex(output, "Tunnel 10, encap IP")
2677 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
2678 self.assertRegex(output, "Peer tunnel 12")
2679
371810d1 2680 output = check_output('ip l2tp show session tid 10 session_id 25')
cff83db9
YW
2681 print(output)
2682 self.assertRegex(output, "Session 25 in tunnel 10")
2683 self.assertRegex(output, "Peer session 26, tunnel 12")
2684 self.assertRegex(output, "interface name: l2tp-ses3")
2685
371810d1 2686 output = check_output('ip l2tp show session tid 10 session_id 27')
cff83db9
YW
2687 print(output)
2688 self.assertRegex(output, "Session 27 in tunnel 10")
2689 self.assertRegex(output, "Peer session 28, tunnel 12")
2690 self.assertRegex(output, "interface name: l2tp-ses4")
2691
be68c2c9 2692class NetworkdNetworkTests(unittest.TestCase, Utilities):
95c74b0a 2693
1f0e3109 2694 def setUp(self):
a962d857 2695 setup_common()
1f0e3109
SS
2696
2697 def tearDown(self):
a962d857 2698 tear_down_common()
1f0e3109 2699
f2bcd324
YW
2700 def verify_address_static(
2701 self,
2702 label1: str,
2703 label2: str,
2704 label3: str,
2705 broadcast1: str,
2706 broadcast2: str,
2707 broadcast3: str,
2708 peer1: str,
2709 peer2: str,
2710 peer3: str,
2711 peer4: str,
2712 peer5: str,
2713 peer6: str,
2714 scope1: str,
2715 scope2: str,
2716 deprecated1: str,
2717 deprecated2: str,
2718 deprecated3: str,
2719 deprecated4: str,
2720 route_metric: int,
2721 flag1: str,
2722 flag2: str,
2723 flag3: str,
2724 flag4: str,
d5adff70
YW
2725 ip4_null_16: str,
2726 ip4_null_24: str,
2727 ip6_null_73: str,
2728 ip6_null_74: str,
f2bcd324
YW
2729 ):
2730 output = check_output('ip address show dev dummy98')
2731 print(output)
2732
2733 # simple settings
4a704501
YW
2734 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
2735 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
2736 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
4a704501
YW
2737 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
2738 self.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output)
2739 self.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output)
f2bcd324
YW
2740
2741 # label
2742 self.assertIn(f'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output)
2743 self.assertIn(f'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output)
2744 self.assertIn(f'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output)
2745
2746 # broadcast
2747 self.assertIn(f'inet 10.4.1.1/24{broadcast1} scope global dummy98', output)
2748 self.assertIn(f'inet 10.4.2.1/24{broadcast2} scope global dummy98', output)
2749 self.assertIn(f'inet 10.4.3.1/24{broadcast3} scope global dummy98', output)
2750
2751 # peer
2752 self.assertIn(f'inet 10.5.1.1{peer1} scope global dummy98', output)
2753 self.assertIn(f'inet 10.5.2.1{peer2} scope global dummy98', output)
2754 self.assertIn(f'inet 10.5.3.1{peer3} scope global dummy98', output)
2755 self.assertIn(f'inet6 2001:db8:0:f103::1{peer4} scope global', output)
2756 self.assertIn(f'inet6 2001:db8:0:f103::2{peer5} scope global', output)
2757 self.assertIn(f'inet6 2001:db8:0:f103::3{peer6} scope global', output)
2758
2759 # scope
2760 self.assertIn(f'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output)
2761 self.assertIn(f'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output)
2762
2763 # lifetime
2764 self.assertIn(f'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output)
2765 self.assertIn(f'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output)
2766 self.assertIn(f'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output)
2767 self.assertIn(f'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output)
2768
2769 # route metric
2770 self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2771 self.assertRegex(output, rf'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2772
2773 output_route = check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2774 print(output_route)
2775 self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route)
2776
2777 output_route = check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2778 print(output_route)
2779 self.assertIn(f'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route)
2780
2781 # flags
2782 self.assertIn(f'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output)
2783 self.assertIn(f'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output)
2784 self.assertIn(f'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output)
2785 self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
2786
2787 # null address
d5adff70
YW
2788 self.assertTrue(ip4_null_16.endswith('.0.1'))
2789 prefix16 = ip4_null_16[:-len('.0.1')]
2790 self.assertTrue(ip4_null_24.endswith('.1'))
2791 prefix24 = ip4_null_24[:-len('.1')]
2792 self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
2793 self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
2794 self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
2795 self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
f2bcd324
YW
2796
2797 # invalid sections
2798 self.assertNotIn('10.4.4.1', output)
2799 self.assertNotIn('10.5.4.1', output)
2800 self.assertNotIn('10.5.5.1', output)
2801 self.assertNotIn('10.8.2.1', output)
2802 self.assertNotIn('10.9.3.1', output)
2803 self.assertNotIn('2001:db8:0:f101::2', output)
2804 self.assertNotIn('2001:db8:0:f103::4', output)
b8102725 2805
d19704cd 2806 # netlabel
f2bcd324 2807 self.check_netlabel('dummy98', r'10\.10\.1\.0/24')
a4640bed 2808
10d670a3 2809 check_json(networkctl_json())
d19704cd
YW
2810
2811 def test_address_static(self):
d19704cd 2812 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
c742d7e8
TM
2813 self.setup_nftset('addr4', 'ipv4_addr')
2814 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2815 self.setup_nftset('ifindex', 'iface_index')
1ce2ffac 2816 start_networkd()
d19704cd 2817
95e1fbba 2818 self.wait_online('dummy98:routable')
d5adff70
YW
2819
2820 ip4_null_16 = None
2821 ip4_null_24 = None
2822 output = check_output('ip -4 --json address show dev dummy98')
2823 for i in json.loads(output)[0]['addr_info']:
2824 if i['label'] == 'subnet16':
2825 ip4_null_16 = i['local']
2826 elif i['label'] == 'subnet24':
2827 ip4_null_24 = i['local']
2828 self.assertTrue(ip4_null_16.endswith('.0.1'))
2829 self.assertTrue(ip4_null_24.endswith('.1'))
2830
2831 ip6_null_73 = None
2832 ip6_null_74 = None
2833 output = check_output('ip -6 --json address show dev dummy98')
2834 for i in json.loads(output)[0]['addr_info']:
2835 if i['prefixlen'] == 73:
2836 ip6_null_73 = i['local']
2837 elif i['prefixlen'] == 74:
2838 ip6_null_74 = i['local']
2839 self.assertTrue(ip6_null_73.endswith(':1'))
2840 self.assertTrue(ip6_null_74.endswith(':1'))
2841
f2bcd324
YW
2842 self.verify_address_static(
2843 label1='label1',
2844 label2='label2',
2845 label3='dummy98',
2846 broadcast1='',
2847 broadcast2=' brd 10.4.2.255',
2848 broadcast3=' brd 10.4.3.63',
2849 peer1=' peer 10.5.1.101/24',
2850 peer2=' peer 10.5.2.101/24',
2851 peer3='/24 brd 10.5.3.255',
2852 peer4=' peer 2001:db8:0:f103::101/128',
2853 peer5=' peer 2001:db8:0:f103::102/128',
2854 peer6='/128',
2855 scope1='global',
2856 scope2='link',
2857 deprecated1='',
2858 deprecated2=' deprecated',
2859 deprecated3='',
2860 deprecated4=' deprecated',
2861 route_metric=128,
2862 flag1=' noprefixroute',
2863 flag2='',
2864 flag3=' noprefixroute',
2865 flag4=' home mngtmpaddr',
d5adff70
YW
2866 ip4_null_16=ip4_null_16,
2867 ip4_null_24=ip4_null_24,
2868 ip6_null_73=ip6_null_73,
2869 ip6_null_74=ip6_null_74,
f2bcd324 2870 )
c742d7e8 2871 # nft set
f432aa90
TM
2872 self.check_nftset('addr4', r'10\.10\.1\.1')
2873 self.check_nftset('network4', r'10\.10\.1\.0/24')
2874 self.check_nftset('ifindex', 'dummy98')
c742d7e8
TM
2875
2876 self.teardown_nftset('addr4', 'network4', 'ifindex')
f2bcd324
YW
2877
2878 copy_network_unit('25-address-static.network.d/10-override.conf')
d19704cd 2879 networkctl_reload()
95e1fbba 2880 self.wait_online('dummy98:routable')
f2bcd324
YW
2881 self.verify_address_static(
2882 label1='new-label1',
2883 label2='dummy98',
2884 label3='new-label3',
2885 broadcast1=' brd 10.4.1.255',
2886 broadcast2='',
2887 broadcast3=' brd 10.4.3.31',
2888 peer1=' peer 10.5.1.102/24',
2889 peer2='/24 brd 10.5.2.255',
2890 peer3=' peer 10.5.3.102/24',
2891 peer4=' peer 2001:db8:0:f103::201/128',
2892 peer5='/128',
2893 peer6=' peer 2001:db8:0:f103::203/128',
2894 scope1='link',
2895 scope2='global',
2896 deprecated1=' deprecated',
2897 deprecated2='',
2898 deprecated3=' deprecated',
2899 deprecated4='',
2900 route_metric=256,
2901 flag1='',
2902 flag2=' noprefixroute',
2903 flag3=' home mngtmpaddr',
2904 flag4=' noprefixroute',
d5adff70
YW
2905 ip4_null_16=ip4_null_16,
2906 ip4_null_24=ip4_null_24,
2907 ip6_null_73=ip6_null_73,
2908 ip6_null_74=ip6_null_74,
f2bcd324 2909 )
d19704cd
YW
2910
2911 networkctl_reconfigure('dummy98')
95e1fbba 2912 self.wait_online('dummy98:routable')
f2bcd324
YW
2913 self.verify_address_static(
2914 label1='new-label1',
2915 label2='dummy98',
2916 label3='new-label3',
2917 broadcast1=' brd 10.4.1.255',
2918 broadcast2='',
2919 broadcast3=' brd 10.4.3.31',
2920 peer1=' peer 10.5.1.102/24',
2921 peer2='/24 brd 10.5.2.255',
2922 peer3=' peer 10.5.3.102/24',
2923 peer4=' peer 2001:db8:0:f103::201/128',
2924 peer5='/128',
2925 peer6=' peer 2001:db8:0:f103::203/128',
2926 scope1='link',
2927 scope2='global',
2928 deprecated1=' deprecated',
2929 deprecated2='',
2930 deprecated3=' deprecated',
2931 deprecated4='',
2932 route_metric=256,
2933 flag1='',
2934 flag2=' noprefixroute',
2935 flag3=' home mngtmpaddr',
2936 flag4=' noprefixroute',
d5adff70
YW
2937 ip4_null_16=ip4_null_16,
2938 ip4_null_24=ip4_null_24,
2939 ip6_null_73=ip6_null_73,
2940 ip6_null_74=ip6_null_74,
f2bcd324 2941 )
d19704cd 2942
40971657
YW
2943 # Tests for #20891.
2944 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
f2bcd324
YW
2945 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2946 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2947 output = check_output('ip address show dev dummy98')
40971657 2948 print(output)
f2bcd324
YW
2949 self.assertNotRegex(output, '10.7.1.1/24 .* deprecated')
2950 self.assertNotRegex(output, '2001:db8:0:f104::1/64 .* deprecated')
40971657 2951
f2bcd324 2952 # 2. reconfigure the interface, and check the deprecated flag is set again
a962d857 2953 networkctl_reconfigure('dummy98')
95e1fbba 2954 self.wait_online('dummy98:routable')
f2bcd324
YW
2955 self.verify_address_static(
2956 label1='new-label1',
2957 label2='dummy98',
2958 label3='new-label3',
2959 broadcast1=' brd 10.4.1.255',
2960 broadcast2='',
2961 broadcast3=' brd 10.4.3.31',
2962 peer1=' peer 10.5.1.102/24',
2963 peer2='/24 brd 10.5.2.255',
2964 peer3=' peer 10.5.3.102/24',
2965 peer4=' peer 2001:db8:0:f103::201/128',
2966 peer5='/128',
2967 peer6=' peer 2001:db8:0:f103::203/128',
2968 scope1='link',
2969 scope2='global',
2970 deprecated1=' deprecated',
2971 deprecated2='',
2972 deprecated3=' deprecated',
2973 deprecated4='',
2974 route_metric=256,
2975 flag1='',
2976 flag2=' noprefixroute',
2977 flag3=' home mngtmpaddr',
2978 flag4=' noprefixroute',
d5adff70
YW
2979 ip4_null_16=ip4_null_16,
2980 ip4_null_24=ip4_null_24,
2981 ip6_null_73=ip6_null_73,
2982 ip6_null_74=ip6_null_74,
f2bcd324 2983 )
40971657 2984
d19704cd
YW
2985 # test for ENOBUFS issue #17012 (with reload)
2986 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2987 networkctl_reload()
95e1fbba 2988 self.wait_online('dummy98:routable')
766f8f38 2989 output = check_output('ip -4 address show dev dummy98')
a962d857 2990 for i in range(1, 254):
4a704501 2991 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
e4783b54 2992
d19704cd
YW
2993 # (with reconfigure)
2994 networkctl_reconfigure('dummy98')
95e1fbba 2995 self.wait_online('dummy98:routable')
d19704cd
YW
2996 output = check_output('ip -4 address show dev dummy98')
2997 for i in range(1, 254):
2998 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3a2ef59d
YW
2999
3000 # test for an empty string assignment for Address= in [Network]
3001 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
3002 networkctl_reload()
95e1fbba 3003 self.wait_online('dummy98:routable')
3a2ef59d
YW
3004 output = check_output('ip -4 address show dev dummy98')
3005 for i in range(1, 254):
3006 self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3007 self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
146726b2 3008
44924431
YW
3009 def test_address_ipv4acd(self):
3010 check_output('ip netns add ns99')
3011 check_output('ip link add veth99 type veth peer veth-peer')
3012 check_output('ip link set veth-peer netns ns99')
3013 check_output('ip link set veth99 up')
3014 check_output('ip netns exec ns99 ip link set veth-peer up')
3015 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
3016
a962d857 3017 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False)
dc7d3c5f 3018 start_networkd()
95e1fbba 3019 self.wait_online('veth99:routable')
dc7d3c5f
YW
3020
3021 output = check_output('ip -4 address show dev veth99')
3022 print(output)
44924431
YW
3023 self.assertNotIn('192.168.100.10/24', output)
3024 self.assertIn('192.168.100.11/24', output)
dc7d3c5f 3025
a962d857
YW
3026 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
3027 networkctl_reload()
37611ccb 3028 self.wait_operstate('veth99', operstate='routable', setup_state='configuring', setup_timeout=10)
44924431
YW
3029
3030 output = check_output('ip -4 address show dev veth99')
dc7d3c5f 3031 print(output)
44924431
YW
3032 self.assertNotIn('192.168.100.10/24', output)
3033 self.assertIn('192.168.100.11/24', output)
dc7d3c5f 3034
21266e60
YW
3035 def test_address_peer_ipv4(self):
3036 # test for issue #17304
a962d857 3037 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
21266e60
YW
3038
3039 for trial in range(2):
3040 if trial == 0:
3041 start_networkd()
3042 else:
3043 restart_networkd()
3044
95e1fbba 3045 self.wait_online('dummy98:routable')
21266e60
YW
3046
3047 output = check_output('ip -4 address show dev dummy98')
3048 self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
3049
c9d223e8
YW
3050 @expectedFailureIfModuleIsNotAvailable('vrf')
3051 def test_prefix_route(self):
a962d857
YW
3052 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3053 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3054 '25-vrf.netdev', '25-vrf.network')
c9d223e8
YW
3055 for trial in range(2):
3056 if trial == 0:
3057 start_networkd()
3058 else:
a962d857 3059 restart_networkd()
c9d223e8 3060
95e1fbba 3061 self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
c9d223e8
YW
3062
3063 output = check_output('ip route show table 42 dev dummy98')
3064 print('### ip route show table 42 dev dummy98')
3065 print(output)
3066 self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
c9d223e8
YW
3067 self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3068 self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3069 self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3070 self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
c9d223e8
YW
3071 self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3072 self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3073 output = check_output('ip -6 route show table 42 dev dummy98')
3074 print('### ip -6 route show table 42 dev dummy98')
3075 print(output)
3076 if trial == 0:
3077 # Kernel's bug?
3078 self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3079 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3080 self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3081 self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3082 self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3083 self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3084 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
beb75dd3 3085 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
c9d223e8
YW
3086
3087 print()
3088
3089 output = check_output('ip route show dev test1')
3090 print('### ip route show dev test1')
3091 print(output)
3092 self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3093 output = check_output('ip route show table local dev test1')
3094 print('### ip route show table local dev test1')
3095 print(output)
3096 self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
c9d223e8
YW
3097 self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3098 self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3099 self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
c9d223e8
YW
3100 self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3101 self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3102 output = check_output('ip -6 route show dev test1')
3103 print('### ip -6 route show dev test1')
3104 print(output)
3105 self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3106 self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3107 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3108 output = check_output('ip -6 route show table local dev test1')
3109 print('### ip -6 route show table local dev test1')
3110 print(output)
3111 self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3112 self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3113 self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3114 self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
beb75dd3 3115 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
c9d223e8 3116
1f0e3109 3117 def test_configure_without_carrier(self):
a962d857 3118 copy_network_unit('11-dummy.netdev')
2cf6fdff 3119 start_networkd()
9bacf431
DS
3120 self.wait_operstate('test1', 'off', '')
3121 check_output('ip link set dev test1 up carrier off')
3122
a962d857 3123 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False)
9bacf431 3124 restart_networkd()
95e1fbba 3125 self.wait_online('test1:no-carrier')
9bacf431
DS
3126
3127 carrier_map = {'on': '1', 'off': '0'}
3128 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3129 for carrier in ['off', 'on', 'off']:
3130 with self.subTest(carrier=carrier):
3131 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3132 check_output(f'ip link set dev test1 carrier {carrier}')
95e1fbba 3133 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
9bacf431 3134
10d670a3 3135 output = networkctl_status('test1')
9bacf431
DS
3136 print(output)
3137 self.assertRegex(output, '192.168.0.15')
3138 self.assertRegex(output, '192.168.0.1')
3139 self.assertRegex(output, routable_map[carrier])
e40a58b5 3140
9bacf431 3141 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
a962d857 3142 copy_network_unit('11-dummy.netdev')
9bacf431
DS
3143 start_networkd()
3144 self.wait_operstate('test1', 'off', '')
3145 check_output('ip link set dev test1 up carrier off')
3146
a962d857 3147 copy_network_unit('25-test1.network')
9bacf431 3148 restart_networkd()
95e1fbba 3149 self.wait_online('test1:no-carrier')
9bacf431
DS
3150
3151 carrier_map = {'on': '1', 'off': '0'}
3152 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3153 for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
3154 with self.subTest(carrier=carrier, have_config=have_config):
3155 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3156 check_output(f'ip link set dev test1 carrier {carrier}')
95e1fbba 3157 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
9bacf431 3158
10d670a3 3159 output = networkctl_status('test1')
9bacf431
DS
3160 print(output)
3161 if have_config:
3162 self.assertRegex(output, '192.168.0.15')
3163 self.assertRegex(output, '192.168.0.1')
3164 else:
3165 self.assertNotRegex(output, '192.168.0.15')
3166 self.assertNotRegex(output, '192.168.0.1')
3167 self.assertRegex(output, routable_map[carrier])
1f0e3109 3168
1f0e3109 3169 def test_routing_policy_rule(self):
a962d857 3170 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2cf6fdff 3171 start_networkd()
95e1fbba 3172 self.wait_online('test1:degraded')
e40a58b5 3173
65c24cd0 3174 output = check_output('ip rule list iif test1 priority 111')
1f0e3109 3175 print(output)
65c24cd0 3176 self.assertRegex(output, '111:')
1f0e3109 3177 self.assertRegex(output, 'from 192.168.100.18')
426654d7 3178 self.assertRegex(output, r'tos (0x08|throughput)\s')
1f0e3109
SS
3179 self.assertRegex(output, 'iif test1')
3180 self.assertRegex(output, 'oif test1')
3181 self.assertRegex(output, 'lookup 7')
3182
65c24cd0
YW
3183 output = check_output('ip rule list iif test1 priority 101')
3184 print(output)
3185 self.assertRegex(output, '101:')
3186 self.assertRegex(output, 'from all')
3187 self.assertRegex(output, 'iif test1')
3188 self.assertRegex(output, 'lookup 9')
3189
3190 output = check_output('ip -6 rule list iif test1 priority 100')
3191 print(output)
3192 self.assertRegex(output, '100:')
3193 self.assertRegex(output, 'from all')
3194 self.assertRegex(output, 'iif test1')
3195 self.assertRegex(output, 'lookup 8')
3196
2e8a32af
HV
3197 output = check_output('ip rule list iif test1 priority 102')
3198 print(output)
3199 self.assertRegex(output, '102:')
3200 self.assertRegex(output, 'from 0.0.0.0/8')
3201 self.assertRegex(output, 'iif test1')
3202 self.assertRegex(output, 'lookup 10')
3203
10d670a3 3204 check_json(networkctl_json())
146726b2 3205
b677774d 3206 def test_routing_policy_rule_issue_11280(self):
a962d857
YW
3207 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3208 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
b677774d 3209
a962d857
YW
3210 for trial in range(3):
3211 restart_networkd(show_logs=(trial > 0))
95e1fbba 3212 self.wait_online('test1:degraded', 'dummy98:degraded')
b677774d 3213
371810d1 3214 output = check_output('ip rule list table 7')
b677774d 3215 print(output)
426654d7 3216 self.assertRegex(output, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
b677774d 3217
371810d1 3218 output = check_output('ip rule list table 8')
b677774d 3219 print(output)
426654d7 3220 self.assertRegex(output, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
b677774d 3221
87adeabf 3222 def test_routing_policy_rule_reconfigure(self):
a962d857 3223 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
87adeabf 3224 start_networkd()
95e1fbba 3225 self.wait_online('test1:degraded')
87adeabf
YW
3226
3227 output = check_output('ip rule list table 1011')
3228 print(output)
49ff3f34
YW
3229 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
3230 self.assertIn('10112: from all oif test1 lookup 1011', output)
3231 self.assertIn('10113: from all iif test1 lookup 1011', output)
3232 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
3233
3234 output = check_output('ip -6 rule list table 1011')
3235 print(output)
3236 self.assertIn('10112: from all oif test1 lookup 1011', output)
3237
a962d857
YW
3238 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3239 networkctl_reload()
95e1fbba 3240 self.wait_online('test1:degraded')
49ff3f34
YW
3241
3242 output = check_output('ip rule list table 1011')
3243 print(output)
3244 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
3245 self.assertIn('10112: from all oif test1 lookup 1011', output)
3246 self.assertIn('10113: from all iif test1 lookup 1011', output)
3247 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
3248
3249 output = check_output('ip -6 rule list table 1011')
3250 print(output)
3251 self.assertNotIn('10112: from all oif test1 lookup 1011', output)
3252 self.assertIn('10113: from all iif test1 lookup 1011', output)
87adeabf 3253
a962d857
YW
3254 call('ip rule delete priority 10111')
3255 call('ip rule delete priority 10112')
3256 call('ip rule delete priority 10113')
3257 call('ip rule delete priority 10114')
3258 call('ip -6 rule delete priority 10113')
87adeabf
YW
3259
3260 output = check_output('ip rule list table 1011')
3261 print(output)
3262 self.assertEqual(output, '')
3263
49ff3f34
YW
3264 output = check_output('ip -6 rule list table 1011')
3265 print(output)
3266 self.assertEqual(output, '')
87adeabf 3267
a962d857 3268 networkctl_reconfigure('test1')
95e1fbba 3269 self.wait_online('test1:degraded')
87adeabf
YW
3270
3271 output = check_output('ip rule list table 1011')
3272 print(output)
49ff3f34
YW
3273 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
3274 self.assertIn('10112: from all oif test1 lookup 1011', output)
3275 self.assertIn('10113: from all iif test1 lookup 1011', output)
3276 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
3277
3278 output = check_output('ip -6 rule list table 1011')
3279 print(output)
3280 self.assertIn('10113: from all iif test1 lookup 1011', output)
87adeabf 3281
d586a2c3 3282 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
926062f0 3283 def test_routing_policy_rule_port_range(self):
a962d857 3284 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
2cf6fdff 3285 start_networkd()
95e1fbba 3286 self.wait_online('test1:degraded')
e40a58b5 3287
371810d1 3288 output = check_output('ip rule')
926062f0
SS
3289 print(output)
3290 self.assertRegex(output, '111')
3291 self.assertRegex(output, 'from 192.168.100.18')
3292 self.assertRegex(output, '1123-1150')
3293 self.assertRegex(output, '3224-3290')
3294 self.assertRegex(output, 'tcp')
3295 self.assertRegex(output, 'lookup 7')
1f0e3109 3296
d586a2c3 3297 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
efecf9cd 3298 def test_routing_policy_rule_invert(self):
a962d857 3299 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
2cf6fdff 3300 start_networkd()
95e1fbba 3301 self.wait_online('test1:degraded')
e40a58b5 3302
371810d1 3303 output = check_output('ip rule')
efecf9cd 3304 print(output)
efecf9cd
SS
3305 self.assertRegex(output, '111')
3306 self.assertRegex(output, 'not.*?from.*?192.168.100.18')
3307 self.assertRegex(output, 'tcp')
3308 self.assertRegex(output, 'lookup 7')
3309
4be1fc84
NC
3310 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3311 def test_routing_policy_rule_l3mdev(self):
3312 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3313 start_networkd()
95e1fbba 3314 self.wait_online('test1:degraded')
4be1fc84
NC
3315
3316 output = check_output('ip rule')
3317 print(output)
3318 self.assertIn('1500: from all lookup [l3mdev-table]', output)
3319 self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
3320
6be8e78e
YW
3321 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3322 def test_routing_policy_rule_uidrange(self):
a962d857 3323 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
6be8e78e 3324 start_networkd()
95e1fbba 3325 self.wait_online('test1:degraded')
6be8e78e
YW
3326
3327 output = check_output('ip rule')
3328 print(output)
3329 self.assertRegex(output, '111')
3330 self.assertRegex(output, 'from 192.168.100.18')
3331 self.assertRegex(output, 'lookup 7')
3332 self.assertRegex(output, 'uidrange 100-200')
3333
1d26d4cd
YW
3334 def _test_route_static(self, manage_foreign_routes):
3335 if not manage_foreign_routes:
3336 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3337
d3779490
YW
3338 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3339 '25-route-static-test1.network', '11-dummy.netdev')
2cf6fdff 3340 start_networkd()
95e1fbba 3341 self.wait_online('dummy98:routable')
e2aea43f 3342
10d670a3 3343 output = networkctl_status('dummy98')
e2aea43f 3344 print(output)
0d34228f 3345
6d60f9db 3346 print('### ip -6 route show dev dummy98')
371810d1 3347 output = check_output('ip -6 route show dev dummy98')
0d34228f 3348 print(output)
0b5dc249
YW
3349 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
3350 self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
180c5116 3351 self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
1f0e3109 3352
d9005dec
YW
3353 print('### ip -6 route show default')
3354 output = check_output('ip -6 route show default')
6d60f9db 3355 print(output)
0b5dc249
YW
3356 self.assertIn('default', output)
3357 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output)
1f0e3109 3358
6d60f9db 3359 print('### ip -4 route show dev dummy98')
371810d1 3360 output = check_output('ip -4 route show dev dummy98')
1f0e3109 3361 print(output)
0b5dc249
YW
3362 self.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output)
3363 self.assertIn('149.10.124.64 proto static scope link', output)
3364 self.assertIn('169.254.0.0/16 proto static scope link metric 2048', output)
902bbdc4
YW
3365 self.assertIn('192.168.1.1 proto static scope link initcwnd 20', output)
3366 self.assertIn('192.168.1.2 proto static scope link initrwnd 30', output)
3367 self.assertIn('192.168.1.3 proto static scope link advmss 30', output)
288f58c0 3368 self.assertIn('192.168.1.4 proto static scope link hoplimit 122', output)
0b5dc249 3369 self.assertIn('multicast 149.10.123.4 proto static', output)
1f0e3109 3370
6d60f9db 3371 print('### ip -4 route show dev dummy98 default')
371810d1 3372 output = check_output('ip -4 route show dev dummy98 default')
6d60f9db 3373 print(output)
0b5dc249
YW
3374 self.assertIn('default via 149.10.125.65 proto static onlink', output)
3375 self.assertIn('default via 149.10.124.64 proto static', output)
3376 self.assertIn('default proto static', output)
5e46ca98 3377 self.assertIn('default via 1.1.8.104 proto static', output)
1f0e3109 3378
6d60f9db
YW
3379 print('### ip -4 route show table local dev dummy98')
3380 output = check_output('ip -4 route show table local dev dummy98')
3381 print(output)
0b5dc249
YW
3382 self.assertIn('local 149.10.123.1 proto static scope host', output)
3383 self.assertIn('anycast 149.10.123.2 proto static scope link', output)
3384 self.assertIn('broadcast 149.10.123.3 proto static scope link', output)
6d60f9db 3385
b4f4f119
YW
3386 print('### ip -4 route show type blackhole')
3387 output = check_output('ip -4 route show type blackhole')
1f0e3109 3388 print(output)
0b5dc249 3389 self.assertIn('blackhole 202.54.1.2 proto static', output)
f5050e48 3390
b4f4f119
YW
3391 print('### ip -4 route show type unreachable')
3392 output = check_output('ip -4 route show type unreachable')
f5050e48 3393 print(output)
0b5dc249 3394 self.assertIn('unreachable 202.54.1.3 proto static', output)
f5050e48 3395
b4f4f119
YW
3396 print('### ip -4 route show type prohibit')
3397 output = check_output('ip -4 route show type prohibit')
f5050e48 3398 print(output)
0b5dc249 3399 self.assertIn('prohibit 202.54.1.4 proto static', output)
f5050e48 3400
452d86a5
YW
3401 print('### ip -6 route show type blackhole')
3402 output = check_output('ip -6 route show type blackhole')
3403 print(output)
3404 self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
3405
3406 print('### ip -6 route show type unreachable')
3407 output = check_output('ip -6 route show type unreachable')
3408 print(output)
3409 self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
3410
3411 print('### ip -6 route show type prohibit')
3412 output = check_output('ip -6 route show type prohibit')
3413 print(output)
3414 self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
3415
a0ce990e
YW
3416 print('### ip route show 192.168.10.1')
3417 output = check_output('ip route show 192.168.10.1')
3418 print(output)
0b5dc249 3419 self.assertIn('192.168.10.1 proto static', output)
d3779490
YW
3420 self.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output)
3421 self.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output)
0b5dc249
YW
3422 self.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output)
3423 self.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output)
a0ce990e
YW
3424
3425 print('### ip route show 192.168.10.2')
3426 output = check_output('ip route show 192.168.10.2')
3427 print(output)
3428 # old ip command does not show IPv6 gateways...
0b5dc249
YW
3429 self.assertIn('192.168.10.2 proto static', output)
3430 self.assertIn('nexthop', output)
d3779490
YW
3431 self.assertIn('dev test1 weight 20', output)
3432 self.assertIn('dev test1 weight 30', output)
0b5dc249
YW
3433 self.assertIn('dev dummy98 weight 10', output)
3434 self.assertIn('dev dummy98 weight 5', output)
a0ce990e
YW
3435
3436 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3437 output = check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3438 print(output)
3439 # old ip command does not show 'nexthop' keyword and weight...
0b5dc249 3440 self.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output)
d3779490
YW
3441 self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output)
3442 self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output)
0b5dc249
YW
3443 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
3444 self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
a0ce990e 3445
10d670a3 3446 check_json(networkctl_json())
146726b2 3447
67150a7b 3448 copy_network_unit('25-address-static.network', copy_dropins=False)
a962d857 3449 networkctl_reload()
95e1fbba 3450 self.wait_online('dummy98:routable')
43d4bc9f
YW
3451
3452 # check all routes managed by Manager are removed
b4f4f119
YW
3453 print('### ip -4 route show type blackhole')
3454 output = check_output('ip -4 route show type blackhole')
43d4bc9f
YW
3455 print(output)
3456 self.assertEqual(output, '')
3457
b4f4f119
YW
3458 print('### ip -4 route show type unreachable')
3459 output = check_output('ip -4 route show type unreachable')
43d4bc9f
YW
3460 print(output)
3461 self.assertEqual(output, '')
3462
b4f4f119
YW
3463 print('### ip -4 route show type prohibit')
3464 output = check_output('ip -4 route show type prohibit')
43d4bc9f
YW
3465 print(output)
3466 self.assertEqual(output, '')
3467
452d86a5
YW
3468 print('### ip -6 route show type blackhole')
3469 output = check_output('ip -6 route show type blackhole')
3470 print(output)
3471 self.assertEqual(output, '')
3472
3473 print('### ip -6 route show type unreachable')
3474 output = check_output('ip -6 route show type unreachable')
3475 print(output)
3476 self.assertEqual(output, '')
3477
3478 print('### ip -6 route show type prohibit')
3479 output = check_output('ip -6 route show type prohibit')
3480 print(output)
3481 self.assertEqual(output, '')
3482
a962d857
YW
3483 remove_network_unit('25-address-static.network')
3484 networkctl_reload()
95e1fbba 3485 self.wait_online('dummy98:routable')
43d4bc9f
YW
3486
3487 # check all routes managed by Manager are reconfigured
b4f4f119
YW
3488 print('### ip -4 route show type blackhole')
3489 output = check_output('ip -4 route show type blackhole')
43d4bc9f 3490 print(output)
0b5dc249 3491 self.assertIn('blackhole 202.54.1.2 proto static', output)
43d4bc9f 3492
b4f4f119
YW
3493 print('### ip -4 route show type unreachable')
3494 output = check_output('ip -4 route show type unreachable')
43d4bc9f 3495 print(output)
0b5dc249 3496 self.assertIn('unreachable 202.54.1.3 proto static', output)
43d4bc9f 3497
b4f4f119
YW
3498 print('### ip -4 route show type prohibit')
3499 output = check_output('ip -4 route show type prohibit')
43d4bc9f 3500 print(output)
0b5dc249 3501 self.assertIn('prohibit 202.54.1.4 proto static', output)
43d4bc9f 3502
452d86a5
YW
3503 print('### ip -6 route show type blackhole')
3504 output = check_output('ip -6 route show type blackhole')
3505 print(output)
3506 self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
3507
3508 print('### ip -6 route show type unreachable')
3509 output = check_output('ip -6 route show type unreachable')
3510 print(output)
3511 self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
3512
3513 print('### ip -6 route show type prohibit')
3514 output = check_output('ip -6 route show type prohibit')
3515 print(output)
3516 self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
3517
a962d857 3518 remove_link('dummy98')
43d4bc9f
YW
3519 time.sleep(2)
3520
3521 # check all routes managed by Manager are removed
b4f4f119
YW
3522 print('### ip -4 route show type blackhole')
3523 output = check_output('ip -4 route show type blackhole')
43d4bc9f
YW
3524 print(output)
3525 self.assertEqual(output, '')
3526
b4f4f119
YW
3527 print('### ip -4 route show type unreachable')
3528 output = check_output('ip -4 route show type unreachable')
43d4bc9f
YW
3529 print(output)
3530 self.assertEqual(output, '')
3531
b4f4f119
YW
3532 print('### ip -4 route show type prohibit')
3533 output = check_output('ip -4 route show type prohibit')
43d4bc9f
YW
3534 print(output)
3535 self.assertEqual(output, '')
3536
452d86a5
YW
3537 print('### ip -6 route show type blackhole')
3538 output = check_output('ip -6 route show type blackhole')
3539 print(output)
3540 self.assertEqual(output, '')
3541
3542 print('### ip -6 route show type unreachable')
3543 output = check_output('ip -6 route show type unreachable')
3544 print(output)
3545 self.assertEqual(output, '')
3546
3547 print('### ip -6 route show type prohibit')
3548 output = check_output('ip -6 route show type prohibit')
3549 print(output)
3550 self.assertEqual(output, '')
3551
1d26d4cd 3552 def test_route_static(self):
a962d857 3553 first = True
1d26d4cd 3554 for manage_foreign_routes in [True, False]:
a962d857
YW
3555 if first:
3556 first = False
3557 else:
3558 self.tearDown()
3559
3560 print(f'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
1d26d4cd
YW
3561 with self.subTest(manage_foreign_routes=manage_foreign_routes):
3562 self._test_route_static(manage_foreign_routes)
3563
297f9d86
YW
3564 @expectedFailureIfRTA_VIAIsNotSupported()
3565 def test_route_via_ipv6(self):
a962d857 3566 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
297f9d86 3567 start_networkd()
95e1fbba 3568 self.wait_online('dummy98:routable')
297f9d86 3569
10d670a3 3570 output = networkctl_status('dummy98')
297f9d86
YW
3571 print(output)
3572
3573 print('### ip -6 route show dev dummy98')
3574 output = check_output('ip -6 route show dev dummy98')
3575 print(output)
3576 self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3577 self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
3578
3579 print('### ip -4 route show dev dummy98')
3580 output = check_output('ip -4 route show dev dummy98')
3581 print(output)
3582 self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3583 self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3584
93e898d6
YW
3585 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3586 def test_route_congctl(self):
3587 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3588 start_networkd()
95e1fbba 3589 self.wait_online('dummy98:routable')
93e898d6
YW
3590
3591 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3592 output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3593 print(output)
3594 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
3595 self.assertIn('congctl dctcp', output)
3596
3597 print('### ip -4 route show dev dummy98 149.10.124.66')
3598 output = check_output('ip -4 route show dev dummy98 149.10.124.66')
3599 print(output)
3600 self.assertIn('149.10.124.66 proto static', output)
3601 self.assertIn('congctl dctcp', output)
1791956e 3602 self.assertIn('rto_min 300s', output)
93e898d6 3603
40afe491
YW
3604 @expectedFailureIfModuleIsNotAvailable('vrf')
3605 def test_route_vrf(self):
a962d857
YW
3606 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3607 '25-vrf.netdev', '25-vrf.network')
40afe491 3608 start_networkd()
95e1fbba 3609 self.wait_online('dummy98:routable', 'vrf99:carrier')
40afe491
YW
3610
3611 output = check_output('ip route show vrf vrf99')
3612 print(output)
3613 self.assertRegex(output, 'default via 192.168.100.1')
3614
3615 output = check_output('ip route show')
3616 print(output)
3617 self.assertNotRegex(output, 'default via 192.168.100.1')
3618
0b1cd3e2 3619 def test_gateway_reconfigure(self):
a962d857 3620 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
0b1cd3e2 3621 start_networkd()
95e1fbba 3622 self.wait_online('dummy98:routable')
0b1cd3e2
WKI
3623 print('### ip -4 route show dev dummy98 default')
3624 output = check_output('ip -4 route show dev dummy98 default')
3625 print(output)
a962d857
YW
3626 self.assertIn('default via 149.10.124.59 proto static', output)
3627 self.assertNotIn('149.10.124.60', output)
0b1cd3e2 3628
a962d857
YW
3629 remove_network_unit('25-gateway-static.network')
3630 copy_network_unit('25-gateway-next-static.network')
3631 networkctl_reload()
95e1fbba 3632 self.wait_online('dummy98:routable')
0b1cd3e2
WKI
3633 print('### ip -4 route show dev dummy98 default')
3634 output = check_output('ip -4 route show dev dummy98 default')
3635 print(output)
a962d857
YW
3636 self.assertNotIn('149.10.124.59', output)
3637 self.assertIn('default via 149.10.124.60 proto static', output)
0b1cd3e2 3638
20ca06a6
DA
3639 def test_ip_route_ipv6_src_route(self):
3640 # a dummy device does not make the addresses go through tentative state, so we
3641 # reuse a bond from an earlier test, which does make the addresses go through
3642 # tentative state, and do our test on that
a962d857 3643 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 3644 start_networkd()
95e1fbba 3645 self.wait_online('dummy98:enslaved', 'bond199:routable')
20ca06a6 3646
371810d1 3647 output = check_output('ip -6 route list dev bond199')
20ca06a6 3648 print(output)
7e305278 3649 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output)
20ca06a6 3650
e4948bb2
YW
3651 def test_route_preferred_source_with_existing_address(self):
3652 # See issue #28009.
3653 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3654 start_networkd()
3655
3656 for i in range(3):
3657 if i != 0:
3658 networkctl_reconfigure('dummy98')
3659
95e1fbba 3660 self.wait_online('dummy98:routable')
e4948bb2
YW
3661
3662 output = check_output('ip -6 route list dev dummy98')
3663 print(output)
3664 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output)
3665
1f0e3109 3666 def test_ip_link_mac_address(self):
a962d857 3667 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
2cf6fdff 3668 start_networkd()
95e1fbba 3669 self.wait_online('dummy98:degraded')
1f0e3109 3670
371810d1 3671 output = check_output('ip link show dummy98')
1f0e3109 3672 print(output)
45aa0e84 3673 self.assertRegex(output, '00:01:02:aa:bb:cc')
1f0e3109
SS
3674
3675 def test_ip_link_unmanaged(self):
a962d857
YW
3676 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3677 start_networkd()
1f0e3109 3678
19cf3143 3679 self.wait_operstate('dummy98', 'off', setup_state='unmanaged')
1f0e3109
SS
3680
3681 def test_ipv6_address_label(self):
a962d857 3682 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
2cf6fdff 3683 start_networkd()
95e1fbba 3684 self.wait_online('dummy98:degraded')
1f0e3109 3685
371810d1 3686 output = check_output('ip addrlabel list')
1f0e3109
SS
3687 print(output)
3688 self.assertRegex(output, '2004:da8:1::/64')
3689
cff0cadc 3690 def test_ipv6_proxy_ndp(self):
a962d857 3691 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
cff0cadc
YW
3692 start_networkd()
3693
95e1fbba 3694 self.wait_online('dummy98:routable')
cff0cadc
YW
3695
3696 output = check_output('ip neighbor show proxy dev dummy98')
3697 print(output)
a962d857 3698 for i in range(1, 5):
cff0cadc
YW
3699 self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
3700
d4c8de21
MM
3701 def test_ipv6_neigh_retrans_time(self):
3702 link='test25'
3703 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3704 start_networkd()
3705
95e1fbba 3706 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3707 remove_network_unit('25-dummy.network')
3708
3709 # expect retrans_time_ms updated
3710 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3711 networkctl_reload()
95e1fbba 3712 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3713 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
3714 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3715
3716 # expect retrans_time_ms unchanged
3717 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3718 networkctl_reload()
95e1fbba 3719 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3720 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
3721 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3722
3723 # expect retrans_time_ms unchanged
3724 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3725 networkctl_reload()
95e1fbba 3726 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3727 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
3728 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3729
3730 # expect retrans_time_ms unchanged
3731 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3732 networkctl_reload()
95e1fbba 3733 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3734 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
3735 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3736
3737 # expect retrans_time_ms unchanged
3738 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3739 networkctl_reload()
95e1fbba 3740 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3741 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
3742 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3743
3744 # expect retrans_time_ms updated
3745 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3746 networkctl_reload()
95e1fbba 3747 self.wait_online(f'{link}:degraded')
d4c8de21
MM
3748 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000')
3749 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3750
9362f7d5
YW
3751 def test_neighbor(self):
3752 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3753 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3754 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3755 copy_dropins=False)
2cf6fdff 3756 start_networkd()
95e1fbba 3757 self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
9362f7d5
YW
3758
3759 print('### ip neigh list dev gretun97')
3760 output = check_output('ip neigh list dev gretun97')
3761 print(output)
3762 self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
3763 self.assertNotIn('10.0.0.23', output)
3764
3765 print('### ip neigh list dev ip6gretun97')
3766 output = check_output('ip neigh list dev ip6gretun97')
3767 print(output)
3768 self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3769 self.assertNotIn('2001:db8:0:f102::18', output)
e4a71bf3 3770
d1bdafd2 3771 print('### ip neigh list dev dummy98')
df7f9afa 3772 output = check_output('ip neigh list dev dummy98')
e4a71bf3 3773 print(output)
2ede3559
YW
3774 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
3775 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
3776 self.assertNotIn('2004:da8:1:0::2', output)
3777 self.assertNotIn('192.168.10.2', output)
3778 self.assertNotIn('00:00:5e:00:02:67', output)
e4a71bf3 3779
10d670a3 3780 check_json(networkctl_json())
146726b2 3781
9362f7d5
YW
3782 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3783 # the valid configurations in 10-step1.conf.
3784 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
2ede3559 3785 networkctl_reload()
95e1fbba 3786 self.wait_online('dummy98:degraded')
2ede3559 3787
9362f7d5 3788 print('### ip neigh list dev dummy98')
2ede3559
YW
3789 output = check_output('ip neigh list dev dummy98')
3790 print(output)
3791 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
3792 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
3793 self.assertNotIn('2004:da8:1:0::2', output)
3794 self.assertNotIn('192.168.10.2', output)
9362f7d5 3795 self.assertNotIn('00:00:5e:00:02:67', output)
d1bdafd2 3796
10d670a3 3797 check_json(networkctl_json())
d1bdafd2 3798
9362f7d5
YW
3799 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3800 '25-neighbor-dummy.network.d/10-step2.conf')
3801 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
a962d857 3802 networkctl_reload()
95e1fbba 3803 self.wait_online('dummy98:degraded')
9362f7d5 3804
d1bdafd2
WKI
3805 print('### ip neigh list dev dummy98')
3806 output = check_output('ip neigh list dev dummy98')
3807 print(output)
9362f7d5 3808 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
2ede3559 3809 self.assertNotIn('00:00:5e:00:02:65', output)
9362f7d5
YW
3810 self.assertNotIn('00:00:5e:00:02:66', output)
3811 self.assertNotIn('00:00:5e:00:03:65', output)
2ede3559 3812 self.assertNotIn('2004:da8:1::1', output)
d1bdafd2 3813
05514ae1 3814 def test_link_local_addressing(self):
a962d857
YW
3815 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3816 '25-link-local-addressing-no.network', '12-dummy.netdev')
2cf6fdff 3817 start_networkd()
95e1fbba 3818 self.wait_online('test1:degraded', 'dummy98:carrier')
05514ae1 3819
371810d1 3820 output = check_output('ip address show dev test1')
05514ae1
YW
3821 print(output)
3822 self.assertRegex(output, 'inet .* scope link')
3823 self.assertRegex(output, 'inet6 .* scope link')
3824
371810d1 3825 output = check_output('ip address show dev dummy98')
05514ae1
YW
3826 print(output)
3827 self.assertNotRegex(output, 'inet6* .* scope link')
3828
f7805a6c
FS
3829 # Documentation/networking/ip-sysctl.txt
3830 #
3831 # addr_gen_mode - INTEGER
3832 # Defines how link-local and autoconf addresses are generated.
3833 #
3834 # 0: generate address based on EUI64 (default)
3835 # 1: do no generate a link-local address, use EUI64 for addresses generated
3836 # from autoconf
3837 # 2: generate stable privacy addresses, using the secret from
3838 # stable_secret (RFC7217)
3839 # 3: generate stable privacy addresses, using a random secret if unset
05514ae1 3840
a962d857
YW
3841 self.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3842 self.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3843 self.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
05514ae1 3844
2becdbcc 3845 def test_link_local_addressing_ipv6ll(self):
a962d857 3846 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
7b3770a7 3847 start_networkd()
95e1fbba 3848 self.wait_online('dummy98:degraded')
7b3770a7 3849
2becdbcc 3850 # An IPv6LL address exists by default.
7b3770a7
YW
3851 output = check_output('ip address show dev dummy98')
3852 print(output)
3853 self.assertRegex(output, 'inet6 .* scope link')
3854
a962d857
YW
3855 copy_network_unit('25-link-local-addressing-no.network')
3856 networkctl_reload()
95e1fbba 3857 self.wait_online('dummy98:carrier')
7b3770a7 3858
2becdbcc 3859 # Check if the IPv6LL address is removed.
7b3770a7
YW
3860 output = check_output('ip address show dev dummy98')
3861 print(output)
2becdbcc
YW
3862 self.assertNotRegex(output, 'inet6 .* scope link')
3863
a962d857
YW
3864 remove_network_unit('25-link-local-addressing-no.network')
3865 networkctl_reload()
95e1fbba 3866 self.wait_online('dummy98:degraded')
2becdbcc
YW
3867
3868 # Check if a new IPv6LL address is assigned.
3869 output = check_output('ip address show dev dummy98')
3870 print(output)
3871 self.assertRegex(output, 'inet6 .* scope link')
7b3770a7 3872
1f0e3109 3873 def test_sysctl(self):
856a247e
YW
3874 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3875 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
2cf6fdff 3876 start_networkd()
95e1fbba 3877 self.wait_online('dummy98:degraded')
ec38833c 3878
a962d857 3879 self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
856a247e 3880 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
a962d857
YW
3881 self.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3882 self.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3883 self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3884 self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3885 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
b4959550 3886 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
a962d857 3887 self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
ab2d9e29 3888 self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
1f0e3109 3889
856a247e
YW
3890 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3891 networkctl_reload()
95e1fbba 3892 self.wait_online('dummy98:degraded')
856a247e
YW
3893
3894 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3895
4da33154 3896 def test_sysctl_disable_ipv6(self):
a962d857 3897 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
4da33154
YW
3898
3899 print('## Disable ipv6')
cefd6b3d
ZJS
3900 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3901 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
4da33154 3902
2cf6fdff 3903 start_networkd()
95e1fbba 3904 self.wait_online('dummy98:routable')
4da33154 3905
371810d1 3906 output = check_output('ip -4 address show dummy98')
4da33154
YW
3907 print(output)
3908 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
371810d1 3909 output = check_output('ip -6 address show dummy98')
4da33154 3910 print(output)
57ad7607
ZJS
3911 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
3912 self.assertRegex(output, 'inet6 .* scope link')
4933b97d
YW
3913 output = check_output('ip -4 route show dev dummy98')
3914 print(output)
3d2c2692 3915 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
d9005dec 3916 output = check_output('ip -6 route show default')
4933b97d 3917 print(output)
d9005dec
YW
3918 self.assertRegex(output, 'default')
3919 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4da33154 3920
a962d857 3921 remove_link('dummy98')
4da33154
YW
3922
3923 print('## Enable ipv6')
cefd6b3d
ZJS
3924 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3925 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
4da33154 3926
a962d857 3927 restart_networkd()
95e1fbba 3928 self.wait_online('dummy98:routable')
4da33154 3929
371810d1 3930 output = check_output('ip -4 address show dummy98')
4da33154
YW
3931 print(output)
3932 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
371810d1 3933 output = check_output('ip -6 address show dummy98')
4da33154 3934 print(output)
4933b97d 3935 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4da33154 3936 self.assertRegex(output, 'inet6 .* scope link')
4933b97d
YW
3937 output = check_output('ip -4 route show dev dummy98')
3938 print(output)
3d2c2692 3939 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
d9005dec 3940 output = check_output('ip -6 route show default')
4933b97d 3941 print(output)
d9005dec 3942 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4da33154 3943
cd65d067 3944 def test_bind_carrier(self):
a962d857 3945 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
2cf6fdff 3946 start_networkd()
cd65d067 3947
3e2f7c46
YW
3948 # no bound interface.
3949 self.wait_operstate('test1', 'off', setup_state='configuring')
371810d1 3950 output = check_output('ip address show test1')
cd65d067 3951 print(output)
3e2f7c46
YW
3952 self.assertNotIn('UP,LOWER_UP', output)
3953 self.assertIn('DOWN', output)
3954 self.assertNotIn('192.168.10', output)
cd65d067 3955
3e2f7c46
YW
3956 # add one bound interface. The interface will be up.
3957 check_output('ip link add dummy98 type dummy')
3958 check_output('ip link set dummy98 up')
95e1fbba 3959 self.wait_online('test1:routable')
3e2f7c46
YW
3960 output = check_output('ip address show test1')
3961 print(output)
3962 self.assertIn('UP,LOWER_UP', output)
3963 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
3964
3965 # add another bound interface. The interface is still up.
cefd6b3d
ZJS
3966 check_output('ip link add dummy99 type dummy')
3967 check_output('ip link set dummy99 up')
fcd79988 3968 self.wait_operstate('dummy99', 'degraded', setup_state='unmanaged')
371810d1 3969 output = check_output('ip address show test1')
cd65d067 3970 print(output)
3e2f7c46
YW
3971 self.assertIn('UP,LOWER_UP', output)
3972 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 3973
3e2f7c46 3974 # remove one of the bound interfaces. The interface is still up
a962d857 3975 remove_link('dummy98')
371810d1 3976 output = check_output('ip address show test1')
cd65d067 3977 print(output)
3e2f7c46
YW
3978 self.assertIn('UP,LOWER_UP', output)
3979 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 3980
3e2f7c46 3981 # bring down the remaining bound interface. The interface will be down.
bc942f69 3982 check_output('ip link set dummy99 down')
3e2f7c46
YW
3983 self.wait_operstate('test1', 'off')
3984 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
371810d1 3985 output = check_output('ip address show test1')
cd65d067 3986 print(output)
3e2f7c46
YW
3987 self.assertNotIn('UP,LOWER_UP', output)
3988 self.assertIn('DOWN', output)
3989 self.assertNotIn('192.168.10', output)
cd65d067 3990
3e2f7c46 3991 # bring up the bound interface. The interface will be up.
bc942f69 3992 check_output('ip link set dummy99 up')
95e1fbba 3993 self.wait_online('test1:routable')
371810d1 3994 output = check_output('ip address show test1')
cd65d067 3995 print(output)
3e2f7c46
YW
3996 self.assertIn('UP,LOWER_UP', output)
3997 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
3998
3999 # remove the remaining bound interface. The interface will be down.
4000 remove_link('dummy99')
4001 self.wait_operstate('test1', 'off')
4002 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4003 output = check_output('ip address show test1')
4004 print(output)
4005 self.assertNotIn('UP,LOWER_UP', output)
4006 self.assertIn('DOWN', output)
4007 self.assertNotIn('192.168.10', output)
4008
4009 # re-add one bound interface. The interface will be up.
4010 check_output('ip link add dummy98 type dummy')
4011 check_output('ip link set dummy98 up')
95e1fbba 4012 self.wait_online('test1:routable')
3e2f7c46
YW
4013 output = check_output('ip address show test1')
4014 print(output)
4015 self.assertIn('UP,LOWER_UP', output)
4016 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 4017
a962d857 4018 def _test_activation_policy(self, interface, test):
2236d75d
DS
4019 conffile = '25-activation-policy.network'
4020 if test:
4021 conffile = f'{conffile}.d/{test}.conf'
ee9918ae 4022 if interface == 'vlan99':
a962d857
YW
4023 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
4024 copy_network_unit('11-dummy.netdev', conffile, copy_dropins=False)
2236d75d
DS
4025 start_networkd()
4026
4027 always = test.startswith('always')
ebb5036f 4028 initial_up = test != 'manual' and not test.endswith('down') # note: default is up
2236d75d
DS
4029 expect_up = initial_up
4030 next_up = not expect_up
4031
cfbdc438 4032 if test.endswith('down'):
ee9918ae 4033 self.wait_activated(interface)
cfbdc438 4034
2236d75d
DS
4035 for iteration in range(4):
4036 with self.subTest(iteration=iteration, expect_up=expect_up):
4037 operstate = 'routable' if expect_up else 'off'
618da3e7 4038 setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None)
ee9918ae 4039 self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20)
2236d75d
DS
4040
4041 if expect_up:
ee9918ae
YW
4042 self.assertIn('UP', check_output(f'ip link show {interface}'))
4043 self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}'))
4044 self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}'))
2236d75d 4045 else:
ee9918ae 4046 self.assertIn('DOWN', check_output(f'ip link show {interface}'))
2236d75d
DS
4047
4048 if next_up:
ee9918ae 4049 check_output(f'ip link set dev {interface} up')
2236d75d 4050 else:
ee9918ae 4051 check_output(f'ip link set dev {interface} down')
2236d75d
DS
4052 expect_up = initial_up if always else next_up
4053 next_up = not next_up
073ad7ed
YW
4054 if always:
4055 time.sleep(1)
2236d75d 4056
2236d75d 4057 def test_activation_policy(self):
a962d857 4058 first = True
ee9918ae 4059 for interface in ['test1', 'vlan99']:
a962d857
YW
4060 for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
4061 if first:
4062 first = False
4063 else:
4064 self.tearDown()
4065
4066 print(f'### test_activation_policy(interface={interface}, test={test})')
4067 with self.subTest(interface=interface, test=test):
4068 self._test_activation_policy(interface, test)
2236d75d 4069
61764fe4 4070 def _test_activation_policy_required_for_online(self, policy, required):
61764fe4
DS
4071 conffile = '25-activation-policy.network'
4072 units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
4073 if policy:
4074 units += [f'{conffile}.d/{policy}.conf']
4075 if required:
4076 units += [f'{conffile}.d/required-{required}.conf']
a962d857 4077 copy_network_unit(*units, copy_dropins=False)
61764fe4
DS
4078 start_networkd()
4079
cfbdc438
YW
4080 if policy.endswith('down'):
4081 self.wait_activated('test1')
4082
61764fe4
DS
4083 if policy.endswith('down') or policy == 'manual':
4084 self.wait_operstate('test1', 'off', setup_state='configuring')
4085 else:
95e1fbba 4086 self.wait_online('test1')
61764fe4
DS
4087
4088 if policy == 'always-down':
4089 # if always-down, required for online is forced to no
4090 expected = False
4091 elif required:
4092 # otherwise if required for online is specified, it should match that
4093 expected = required == 'yes'
4094 elif policy:
4095 # otherwise if only policy specified, required for online defaults to
4096 # true if policy is up, always-up, or bound
4097 expected = policy.endswith('up') or policy == 'bound'
4098 else:
4099 # default is true, if neither are specified
4100 expected = True
4101
10d670a3 4102 output = networkctl_status('test1')
61764fe4
DS
4103 print(output)
4104
4105 yesno = 'yes' if expected else 'no'
4106 self.assertRegex(output, f'Required For Online: {yesno}')
4107
61764fe4 4108 def test_activation_policy_required_for_online(self):
a962d857 4109 first = True
61764fe4
DS
4110 for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
4111 for required in ['yes', 'no', '']:
a962d857
YW
4112 if first:
4113 first = False
4114 else:
4115 self.tearDown()
4116
4117 print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})')
61764fe4
DS
4118 with self.subTest(policy=policy, required=required):
4119 self._test_activation_policy_required_for_online(policy, required)
4120
fdcd1ec5 4121 def test_domain(self):
a962d857 4122 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
2cf6fdff 4123 start_networkd()
95e1fbba 4124 self.wait_online('dummy98:routable')
fdcd1ec5 4125
10d670a3 4126 output = networkctl_status('dummy98')
fdcd1ec5
YW
4127 print(output)
4128 self.assertRegex(output, 'Address: 192.168.42.100')
4129 self.assertRegex(output, 'DNS: 192.168.42.1')
4130 self.assertRegex(output, 'Search Domains: one')
4131
1e498853 4132 def test_keep_configuration_static(self):
1e498853
YW
4133 check_output('ip link add name dummy98 type dummy')
4134 check_output('ip address add 10.1.2.3/16 dev dummy98')
4135 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
4136 output = check_output('ip address show dummy98')
4137 print(output)
4138 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
4139 self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4140 output = check_output('ip route show dev dummy98')
4141 print(output)
4142
a962d857 4143 copy_network_unit('24-keep-configuration-static.network')
2cf6fdff 4144 start_networkd()
95e1fbba 4145 self.wait_online('dummy98:routable')
1e498853
YW
4146
4147 output = check_output('ip address show dummy98')
4148 print(output)
4149 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
4150 self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4151
78265b5b 4152 def check_nexthop(self, manage_foreign_nexthops, first):
95e1fbba 4153 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
086bcf5d 4154
e7660b9a
YW
4155 output = check_output('ip nexthop list dev veth99')
4156 print(output)
78265b5b
YW
4157 if first:
4158 self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
4159 self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
4160 else:
4161 self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
4162 self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output)
e7660b9a
YW
4163 self.assertIn('id 3 dev veth99', output)
4164 self.assertIn('id 4 dev veth99', output)
78265b5b
YW
4165 if first:
4166 self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
4167 else:
4168 self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
4169 self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
e7660b9a 4170 self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
41231f26
YW
4171 if manage_foreign_nexthops:
4172 self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
086bcf5d 4173
e7660b9a
YW
4174 output = check_output('ip nexthop list dev dummy98')
4175 print(output)
78265b5b
YW
4176 if first:
4177 self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
4178 else:
4179 self.assertIn('id 21 via 192.168.20.1 dev dummy98', output)
41231f26
YW
4180 if manage_foreign_nexthops:
4181 self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
4182 else:
4183 self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
69a91c70 4184
e7660b9a
YW
4185 # kernel manages blackhole nexthops on lo
4186 output = check_output('ip nexthop list dev lo')
4187 print(output)
78265b5b
YW
4188 if first:
4189 self.assertIn('id 6 blackhole', output)
4190 self.assertIn('id 7 blackhole', output)
4191 else:
4192 self.assertIn('id 1 blackhole', output)
4193 self.assertIn('id 2 blackhole', output)
cee0f719 4194
e7660b9a 4195 # group nexthops are shown with -0 option
78265b5b
YW
4196 if first:
4197 output = check_output('ip -0 nexthop list id 21')
4198 print(output)
4199 self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
4200 else:
4201 output = check_output('ip -0 nexthop list id 20')
4202 print(output)
4203 self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)')
cee0f719 4204
e7660b9a
YW
4205 output = check_output('ip route show dev veth99 10.10.10.10')
4206 print(output)
78265b5b
YW
4207 if first:
4208 self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
4209 else:
4210 self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output)
e2d9bc5c 4211
e7660b9a
YW
4212 output = check_output('ip route show dev veth99 10.10.10.11')
4213 print(output)
78265b5b
YW
4214 if first:
4215 self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
4216 else:
4217 self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output)
cee0f719 4218
e7660b9a
YW
4219 output = check_output('ip route show dev veth99 10.10.10.12')
4220 print(output)
78265b5b
YW
4221 if first:
4222 self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
4223 else:
4224 self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output)
69a91c70 4225
e7660b9a
YW
4226 output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4227 print(output)
78265b5b
YW
4228 if first:
4229 self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
4230 else:
4231 self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
9c8f90d0 4232
e7660b9a
YW
4233 output = check_output('ip route show 10.10.10.13')
4234 print(output)
78265b5b
YW
4235 if first:
4236 self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
4237 else:
4238 self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output)
9c8f90d0 4239
e7660b9a
YW
4240 output = check_output('ip -6 route show 2001:1234:5:8f62::2')
4241 print(output)
78265b5b
YW
4242 if first:
4243 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
4244 else:
4245 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output)
9c8f90d0 4246
e7660b9a
YW
4247 output = check_output('ip route show 10.10.10.14')
4248 print(output)
78265b5b
YW
4249 if first:
4250 self.assertIn('10.10.10.14 nhid 21 proto static', output)
4251 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
4252 else:
4253 self.assertIn('10.10.10.14 nhid 20 proto static', output)
4254 self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output)
e7660b9a 4255 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
146726b2 4256
b5edf3a9
YW
4257 output = networkctl_json()
4258 check_json(output)
4259 self.assertNotIn('"Destination":[10.10.10.14]', output)
e7660b9a 4260
41231f26
YW
4261 def _test_nexthop(self, manage_foreign_nexthops):
4262 if not manage_foreign_nexthops:
4263 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4264
4265 check_output('ip link add dummy98 type dummy')
4266 check_output('ip link set dummy98 up')
4267 check_output('ip address add 192.168.20.20/24 dev dummy98')
4268 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4269
78265b5b
YW
4270 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4271 '12-dummy.netdev', '25-nexthop-dummy-1.network')
9c8f90d0
YW
4272 start_networkd()
4273
78265b5b
YW
4274 self.check_nexthop(manage_foreign_nexthops, first=True)
4275
4276 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4277 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4278 networkctl_reload()
4279 self.check_nexthop(manage_foreign_nexthops, first=False)
69a91c70 4280
78265b5b 4281 remove_network_unit('25-nexthop-2.network')
a962d857
YW
4282 copy_network_unit('25-nexthop-nothing.network')
4283 networkctl_reload()
95e1fbba 4284 self.wait_online('veth99:routable', 'veth-peer:routable')
932e157b 4285
9947c7ba
YW
4286 output = check_output('ip nexthop list dev veth99')
4287 print(output)
4288 self.assertEqual(output, '')
4289 output = check_output('ip nexthop list dev lo')
4290 print(output)
4291 self.assertEqual(output, '')
4292
78265b5b
YW
4293 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4294 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
2ec0e95e
YW
4295 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4296 # here to test reconfiguring with different .network files does not trigger race.
4297 # See also comments in link_drop_requests().
4298 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4299 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
9947c7ba 4300
78265b5b 4301 self.check_nexthop(manage_foreign_nexthops, first=True)
932e157b 4302
f9b5c276
YW
4303 # Remove nexthop with ID 20
4304 check_output('ip nexthop del id 20')
4305 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4306 networkctl_reload()
4307
4308 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4309 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4310 # hence test1 should be stuck in the configuring state.
4311 self.wait_operstate('test1', operstate='routable', setup_state='configuring')
4312
4313 # Wait for a while, and check if the interface is still in the configuring state.
4314 time.sleep(1)
4315 output = networkctl_status('test1')
4316 self.assertIn('State: routable (configuring)', output)
4317
b5edf3a9
YW
4318 # Check if the route which needs nexthop 20 and 21 are forgotten.
4319 output = networkctl_json()
4320 check_json(output)
4321 self.assertNotIn('"Destination":[10.10.10.14]', output)
4322
f9b5c276
YW
4323 # Reconfigure the interface that has nexthop with ID 20 and 21,
4324 # then the route requested by test1 can be configured.
4325 networkctl_reconfigure('dummy98')
95e1fbba 4326 self.wait_online('test1:routable')
f9b5c276
YW
4327
4328 # Check if the requested route actually configured.
4329 output = check_output('ip route show 10.10.11.10')
4330 print(output)
4331 self.assertIn('10.10.11.10 nhid 21 proto static', output)
4332 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
4333 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
4334
a962d857 4335 remove_link('veth99')
9947c7ba
YW
4336 time.sleep(2)
4337
4338 output = check_output('ip nexthop list dev lo')
4339 print(output)
4340 self.assertEqual(output, '')
4341
41231f26
YW
4342 @expectedFailureIfNexthopIsNotAvailable()
4343 def test_nexthop(self):
4344 first = True
4345 for manage_foreign_nexthops in [True, False]:
4346 if first:
4347 first = False
4348 else:
4349 self.tearDown()
4350
4351 print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4352 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
4353 self._test_nexthop(manage_foreign_nexthops)
4354
23b38192
YW
4355class NetworkdTCTests(unittest.TestCase, Utilities):
4356
4357 def setUp(self):
4358 setup_common()
4359
4360 def tearDown(self):
4361 tear_down_common()
4362
4c7d13f4
YW
4363 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4364 def test_qdisc_cake(self):
4365 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
ef3c8a92 4366 start_networkd()
95e1fbba 4367 self.wait_online('dummy98:routable')
ef3c8a92 4368
4c7d13f4 4369 output = check_output('tc qdisc show dev dummy98')
f1de1eb3 4370 print(output)
4c7d13f4
YW
4371 self.assertIn('qdisc cake 3a: root', output)
4372 self.assertIn('bandwidth 500Mbit', output)
4373 self.assertIn('autorate-ingress', output)
4374 self.assertIn('diffserv8', output)
4375 self.assertIn('dual-dsthost', output)
4376 self.assertIn(' nat', output)
4377 self.assertIn(' wash', output)
4378 self.assertIn(' split-gso', output)
4379 self.assertIn(' raw', output)
4380 self.assertIn(' atm', output)
4381 self.assertIn('overhead 128', output)
4382 self.assertIn('mpu 20', output)
4383 self.assertIn('fwmark 0xff00', output)
77d5f36d
YW
4384 self.assertIn('rtt 1s', output)
4385 self.assertIn('ack-filter-aggressive', output)
4c7d13f4
YW
4386
4387 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4388 def test_qdisc_codel(self):
4389 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4390 start_networkd()
95e1fbba 4391 self.wait_online('dummy98:routable')
f1de1eb3 4392
ef3c8a92
YW
4393 output = check_output('tc qdisc show dev dummy98')
4394 print(output)
4c7d13f4
YW
4395 self.assertRegex(output, 'qdisc codel 33: root')
4396 self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
f1de1eb3 4397
4c7d13f4
YW
4398 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4399 def test_qdisc_drr(self):
4400 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4401 start_networkd()
95e1fbba 4402 self.wait_online('dummy98:routable')
f1de1eb3 4403
4c7d13f4
YW
4404 output = check_output('tc qdisc show dev dummy98')
4405 print(output)
4406 self.assertRegex(output, 'qdisc drr 2: root')
4407 output = check_output('tc class show dev dummy98')
4408 print(output)
4409 self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
ef3c8a92 4410
4c7d13f4
YW
4411 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4412 def test_qdisc_ets(self):
4413 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4414 start_networkd()
95e1fbba 4415 self.wait_online('dummy98:routable')
4c7d13f4
YW
4416
4417 output = check_output('tc qdisc show dev dummy98')
4418 print(output)
4419
4420 self.assertRegex(output, 'qdisc ets 3a: root')
4421 self.assertRegex(output, 'bands 10 strict 3')
4422 self.assertRegex(output, 'quanta 1 2 3 4 5')
4423 self.assertRegex(output, 'priomap 3 4 5 6 7')
4424
4425 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4426 def test_qdisc_fq(self):
4427 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4428 start_networkd()
95e1fbba 4429 self.wait_online('dummy98:routable')
0baddbd5 4430
4c7d13f4
YW
4431 output = check_output('tc qdisc show dev dummy98')
4432 print(output)
4433 self.assertRegex(output, 'qdisc fq 32: root')
a05a6e8b
YW
4434 self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4435 self.assertRegex(output, 'quantum 1500')
4436 self.assertRegex(output, 'initial_quantum 13000')
4437 self.assertRegex(output, 'maxrate 1Mbit')
ab9dc1db 4438
4c7d13f4
YW
4439 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4440 def test_qdisc_fq_codel(self):
4441 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4442 start_networkd()
95e1fbba 4443 self.wait_online('dummy98:routable')
ab9dc1db 4444
4c7d13f4
YW
4445 output = check_output('tc qdisc show dev dummy98')
4446 print(output)
4447 self.assertRegex(output, 'qdisc fq_codel 34: root')
7887e580 4448 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')
ab9dc1db 4449
4c7d13f4
YW
4450 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4451 def test_qdisc_fq_pie(self):
4452 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4453 start_networkd()
95e1fbba 4454 self.wait_online('dummy98:routable')
ab9dc1db 4455
4c7d13f4
YW
4456 output = check_output('tc qdisc show dev dummy98')
4457 print(output)
3d55b5a9 4458
4c7d13f4
YW
4459 self.assertRegex(output, 'qdisc fq_pie 3a: root')
4460 self.assertRegex(output, 'limit 200000p')
bc0769c9 4461
4c7d13f4
YW
4462 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4463 def test_qdisc_gred(self):
4464 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4465 start_networkd()
95e1fbba 4466 self.wait_online('dummy98:routable')
4c7d13f4
YW
4467
4468 output = check_output('tc qdisc show dev dummy98')
4469 print(output)
4470 self.assertRegex(output, 'qdisc gred 38: root')
95edcf3f
YW
4471 self.assertRegex(output, 'vqs 12 default 10 grio')
4472
4c7d13f4
YW
4473 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4474 def test_qdisc_hhf(self):
4475 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4476 start_networkd()
95e1fbba 4477 self.wait_online('dummy98:routable')
4c7d13f4
YW
4478
4479 output = check_output('tc qdisc show dev dummy98')
4480 print(output)
4481 self.assertRegex(output, 'qdisc hhf 3a: root')
4482 self.assertRegex(output, 'limit 1022p')
4483
4484 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4485 def test_qdisc_htb_fifo(self):
4486 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4487 start_networkd()
95e1fbba 4488 self.wait_online('dummy98:routable')
4c7d13f4
YW
4489
4490 output = check_output('tc qdisc show dev dummy98')
4491 print(output)
4492 self.assertRegex(output, 'qdisc htb 2: root')
4493 self.assertRegex(output, r'default (0x30|30)')
4494
4495 self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
4496 self.assertRegex(output, 'limit 100000p')
f2c5c129 4497
7b1a31a3
YW
4498 self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
4499 self.assertRegex(output, 'limit 1000000')
4500
73136507
YW
4501 self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4502 self.assertRegex(output, 'limit 1023p')
4503
41bb371b
YW
4504 self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
4505
2ee7e54b 4506 output = check_output('tc -d class show dev dummy98')
ab9dc1db 4507 print(output)
8e2449a5
YW
4508 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4509 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4510 # which is fixed in v6.3.0 by
4511 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4512 self.assertRegex(output, 'class htb 2:37 root leaf 37(:|prio) ')
4513 self.assertRegex(output, 'class htb 2:3a root leaf 3a(:|prio) ')
4514 self.assertRegex(output, 'class htb 2:3b root leaf 3b(:|prio) ')
4515 self.assertRegex(output, 'class htb 2:3c root leaf 3c(:|prio) ')
2ee7e54b
YW
4516 self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4517 self.assertRegex(output, 'burst 123456')
4518 self.assertRegex(output, 'cburst 123457')
0baddbd5 4519
4c7d13f4
YW
4520 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4521 def test_qdisc_ingress(self):
4522 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4523 '25-qdisc-ingress.network', '11-dummy.netdev')
557fa421 4524 start_networkd()
95e1fbba 4525 self.wait_online('dummy98:routable', 'test1:routable')
557fa421
YW
4526
4527 output = check_output('tc qdisc show dev dummy98')
4528 print(output)
4c7d13f4 4529 self.assertRegex(output, 'qdisc clsact')
557fa421 4530
891ff963
YW
4531 output = check_output('tc qdisc show dev test1')
4532 print(output)
4c7d13f4 4533 self.assertRegex(output, 'qdisc ingress')
891ff963 4534
4c7d13f4
YW
4535 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4536 def test_qdisc_netem(self):
4537 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4538 '25-qdisc-netem-compat.network', '11-dummy.netdev')
3d55b5a9 4539 start_networkd()
95e1fbba 4540 self.wait_online('dummy98:routable', 'test1:routable')
3d55b5a9
YW
4541
4542 output = check_output('tc qdisc show dev dummy98')
4543 print(output)
4c7d13f4
YW
4544 self.assertRegex(output, 'qdisc netem 30: root')
4545 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4546
4547 output = check_output('tc qdisc show dev test1')
4548 print(output)
4549 self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
4550 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
3d55b5a9 4551
854f9899 4552 @expectedFailureIfModuleIsNotAvailable('sch_pie')
be94e591 4553 def test_qdisc_pie(self):
a962d857 4554 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
be94e591 4555 start_networkd()
95e1fbba 4556 self.wait_online('dummy98:routable')
be94e591
YW
4557
4558 output = check_output('tc qdisc show dev dummy98')
4559 print(output)
4560 self.assertRegex(output, 'qdisc pie 3a: root')
4561 self.assertRegex(output, 'limit 200000')
4562
4c7d13f4
YW
4563 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4564 def test_qdisc_qfq(self):
4565 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
970ab1fc 4566 start_networkd()
95e1fbba 4567 self.wait_online('dummy98:routable')
970ab1fc
YW
4568
4569 output = check_output('tc qdisc show dev dummy98')
4570 print(output)
4c7d13f4
YW
4571 self.assertRegex(output, 'qdisc qfq 2: root')
4572 output = check_output('tc class show dev dummy98')
4573 print(output)
4574 self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
4575 self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
970ab1fc 4576
4c7d13f4
YW
4577 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4578 def test_qdisc_sfb(self):
4579 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
b753e835 4580 start_networkd()
95e1fbba 4581 self.wait_online('dummy98:routable')
b753e835
YW
4582
4583 output = check_output('tc qdisc show dev dummy98')
4584 print(output)
4c7d13f4
YW
4585 self.assertRegex(output, 'qdisc sfb 39: root')
4586 self.assertRegex(output, 'limit 200000')
1578266b 4587
4c7d13f4
YW
4588 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4589 def test_qdisc_sfq(self):
4590 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4591 start_networkd()
95e1fbba 4592 self.wait_online('dummy98:routable')
b753e835 4593
4c7d13f4
YW
4594 output = check_output('tc qdisc show dev dummy98')
4595 print(output)
4596 self.assertRegex(output, 'qdisc sfq 36: root')
4597 self.assertRegex(output, 'perturb 5sec')
4598
4599 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4600 def test_qdisc_tbf(self):
4601 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
1578266b 4602 start_networkd()
95e1fbba 4603 self.wait_online('dummy98:routable')
1578266b
YW
4604
4605 output = check_output('tc qdisc show dev dummy98')
4606 print(output)
4c7d13f4
YW
4607 self.assertRegex(output, 'qdisc tbf 35: root')
4608 self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
1578266b 4609
4c7d13f4
YW
4610 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4611 def test_qdisc_teql(self):
4612 call_quiet('rmmod sch_teql')
4613
4614 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4615 start_networkd()
4616 self.wait_links('dummy98')
4617 check_output('modprobe sch_teql max_equalizers=2')
95e1fbba 4618 self.wait_online('dummy98:routable')
4c7d13f4
YW
4619
4620 output = check_output('tc qdisc show dev dummy98')
4621 print(output)
4622 self.assertRegex(output, 'qdisc teql1 31: root')
1578266b 4623
e6fa9119
YW
4624 @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
4625 def test_qdisc_drop(self):
4626 copy_network_unit('12-dummy.netdev', '12-dummy.network')
4627 start_networkd()
4628 self.wait_online('dummy98:routable')
4629
4630 # Test case for issue #32247 and #32254.
4631 for _ in range(20):
4632 check_output('tc qdisc replace dev dummy98 root fq')
4633 self.assertFalse(networkd_is_failed())
4634 check_output('tc qdisc replace dev dummy98 root fq pacing')
4635 self.assertFalse(networkd_is_failed())
4636 check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
4637 self.assertFalse(networkd_is_failed())
4638 check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
4639 self.assertFalse(networkd_is_failed())
4640
336d18f0 4641class NetworkdStateFileTests(unittest.TestCase, Utilities):
336d18f0
YW
4642
4643 def setUp(self):
a962d857 4644 setup_common()
336d18f0
YW
4645
4646 def tearDown(self):
a962d857 4647 tear_down_common()
336d18f0
YW
4648
4649 def test_state_file(self):
a962d857 4650 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
336d18f0 4651 start_networkd()
95e1fbba 4652 self.wait_online('dummy98:routable')
336d18f0 4653
f91b2340 4654 # make link state file updated
10d670a3 4655 resolvectl('revert', 'dummy98')
336d18f0 4656
10d670a3 4657 check_json(networkctl_json())
94f0bd62 4658
1a8e1d78
YW
4659 output = read_link_state_file('dummy98')
4660 print(output)
4661 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
4662 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
4663 self.assertIn('ADMIN_STATE=configured', output)
4664 self.assertIn('OPER_STATE=routable', output)
4665 self.assertIn('REQUIRED_FOR_ONLINE=yes', output)
4666 self.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output)
4667 self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output)
4668 self.assertIn('ACTIVATION_POLICY=up', output)
4669 self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output)
4670 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
4671 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4672 self.assertIn('DOMAINS=hogehoge', output)
4673 self.assertIn('ROUTE_DOMAINS=foofoo', output)
4674 self.assertIn('LLMNR=no', output)
4675 self.assertIn('MDNS=yes', output)
4676 self.assertIn('DNSSEC=no', output)
336d18f0 4677
10d670a3
YW
4678 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4679 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4680 resolvectl('llmnr', 'dummy98', 'yes')
4681 resolvectl('mdns', 'dummy98', 'no')
4682 resolvectl('dnssec', 'dummy98', 'yes')
4683 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
336d18f0 4684
10d670a3 4685 check_json(networkctl_json())
94f0bd62 4686
1a8e1d78
YW
4687 output = read_link_state_file('dummy98')
4688 print(output)
4689 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
4690 self.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output)
4691 self.assertIn('DOMAINS=hogehogehoge', output)
4692 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
4693 self.assertIn('LLMNR=yes', output)
4694 self.assertIn('MDNS=no', output)
4695 self.assertIn('DNSSEC=yes', output)
336d18f0 4696
10d670a3 4697 timedatectl('revert', 'dummy98')
336d18f0 4698
10d670a3 4699 check_json(networkctl_json())
94f0bd62 4700
1a8e1d78
YW
4701 output = read_link_state_file('dummy98')
4702 print(output)
4703 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
4704 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4705 self.assertIn('DOMAINS=hogehogehoge', output)
4706 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
4707 self.assertIn('LLMNR=yes', output)
4708 self.assertIn('MDNS=no', output)
4709 self.assertIn('DNSSEC=yes', output)
336d18f0 4710
10d670a3 4711 resolvectl('revert', 'dummy98')
336d18f0 4712
10d670a3 4713 check_json(networkctl_json())
94f0bd62 4714
1a8e1d78
YW
4715 output = read_link_state_file('dummy98')
4716 print(output)
4717 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
4718 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4719 self.assertIn('DOMAINS=hogehoge', output)
4720 self.assertIn('ROUTE_DOMAINS=foofoo', output)
4721 self.assertIn('LLMNR=no', output)
4722 self.assertIn('MDNS=yes', output)
4723 self.assertIn('DNSSEC=no', output)
336d18f0 4724
ee3cbfdb
YW
4725 def test_address_state(self):
4726 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4727 start_networkd()
4728
95e1fbba 4729 self.wait_online('dummy98:degraded')
ee3cbfdb
YW
4730
4731 output = read_link_state_file('dummy98')
4732 self.assertIn('IPV4_ADDRESS_STATE=off', output)
4733 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
4734
4735 # with a routable IPv4 address
4736 check_output('ip address add 10.1.2.3/16 dev dummy98')
95e1fbba
YW
4737 self.wait_online('dummy98:routable', ipv4=True)
4738 self.wait_online('dummy98:routable')
ee3cbfdb
YW
4739
4740 output = read_link_state_file('dummy98')
4741 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
4742 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
4743
4744 check_output('ip address del 10.1.2.3/16 dev dummy98')
4745
4746 # with a routable IPv6 address
4747 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
95e1fbba
YW
4748 self.wait_online('dummy98:routable', ipv6=True)
4749 self.wait_online('dummy98:routable')
ee3cbfdb
YW
4750
4751 output = read_link_state_file('dummy98')
4752 self.assertIn('IPV4_ADDRESS_STATE=off', output)
4753 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
4754
be68c2c9 4755class NetworkdBondTests(unittest.TestCase, Utilities):
c3a8853f
YW
4756
4757 def setUp(self):
a962d857 4758 setup_common()
c3a8853f
YW
4759
4760 def tearDown(self):
a962d857 4761 tear_down_common()
c3a8853f 4762
b06469a6
YW
4763 def test_bond_keep_master(self):
4764 check_output('ip link add bond199 type bond mode active-backup')
4765 check_output('ip link add dummy98 type dummy')
4766 check_output('ip link set dummy98 master bond199')
4767
a962d857 4768 copy_network_unit('23-keep-master.network')
b06469a6 4769 start_networkd()
95e1fbba 4770 self.wait_online('dummy98:enslaved')
b06469a6
YW
4771
4772 output = check_output('ip -d link show bond199')
4773 print(output)
4774 self.assertRegex(output, 'active_slave dummy98')
4775
4776 output = check_output('ip -d link show dummy98')
4777 print(output)
4778 self.assertRegex(output, 'master bond199')
4779
c2990ec3 4780 def test_bond_active_slave(self):
a962d857 4781 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 4782 start_networkd()
95e1fbba 4783 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 4784
371810d1 4785 output = check_output('ip -d link show bond199')
c2990ec3 4786 print(output)
b448fc0a 4787 self.assertIn('active_slave dummy98', output)
c2990ec3 4788
2bb1d3c1
YW
4789 # test case for issue #31165.
4790 since = datetime.datetime.now()
4791 networkctl_reconfigure('dummy98')
95e1fbba 4792 self.wait_online('dummy98:enslaved', 'bond199:degraded')
2bb1d3c1
YW
4793 self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
4794
c2990ec3 4795 def test_bond_primary_slave(self):
a962d857 4796 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 4797 start_networkd()
95e1fbba 4798 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 4799
371810d1 4800 output = check_output('ip -d link show bond199')
c2990ec3 4801 print(output)
b448fc0a
YW
4802 self.assertIn('primary dummy98', output)
4803
4804 # for issue #25627
4805 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
4806 for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4807 with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
4808 f.write(f'[Link]\nMACAddress={mac}\n')
4809
4810 networkctl_reload()
95e1fbba 4811 self.wait_online('dummy98:enslaved', 'bond199:degraded')
b448fc0a
YW
4812
4813 output = check_output('ip -d link show bond199')
4814 print(output)
4815 self.assertIn(f'link/ether {mac}', output)
c2990ec3 4816
cc3e488c 4817 def test_bond_operstate(self):
a962d857
YW
4818 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4819 '25-bond99.network', '25-bond-slave.network')
2cf6fdff 4820 start_networkd()
95e1fbba 4821 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
c3a8853f 4822
371810d1 4823 output = check_output('ip -d link show dummy98')
c3a8853f 4824 print(output)
cc3e488c 4825 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
c3a8853f 4826
371810d1 4827 output = check_output('ip -d link show test1')
c3a8853f
YW
4828 print(output)
4829 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
4830
371810d1 4831 output = check_output('ip -d link show bond99')
c3a8853f
YW
4832 print(output)
4833 self.assertRegex(output, 'MASTER,UP,LOWER_UP')
4834
19cf3143
DS
4835 self.wait_operstate('dummy98', 'enslaved')
4836 self.wait_operstate('test1', 'enslaved')
4837 self.wait_operstate('bond99', 'routable')
c3a8853f 4838
cefd6b3d 4839 check_output('ip link set dummy98 down')
c3a8853f 4840
19cf3143
DS
4841 self.wait_operstate('dummy98', 'off')
4842 self.wait_operstate('test1', 'enslaved')
cf4dbd84 4843 self.wait_operstate('bond99', 'routable')
c3a8853f 4844
cefd6b3d 4845 check_output('ip link set dummy98 up')
c3a8853f 4846
19cf3143
DS
4847 self.wait_operstate('dummy98', 'enslaved')
4848 self.wait_operstate('test1', 'enslaved')
4849 self.wait_operstate('bond99', 'routable')
c3a8853f 4850
cefd6b3d
ZJS
4851 check_output('ip link set dummy98 down')
4852 check_output('ip link set test1 down')
cc3e488c 4853
19cf3143
DS
4854 self.wait_operstate('dummy98', 'off')
4855 self.wait_operstate('test1', 'off')
2700d2c7 4856
a4632dc7 4857 if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
2700d2c7
YW
4858 # Huh? Kernel does not recognize that all slave interfaces are down?
4859 # Let's confirm that networkd's operstate is consistent with ip's result.
4860 self.assertNotRegex(output, 'NO-CARRIER')
cc3e488c 4861
be68c2c9 4862class NetworkdBridgeTests(unittest.TestCase, Utilities):
8d17c386 4863
1f0e3109 4864 def setUp(self):
a962d857 4865 setup_common()
1f0e3109
SS
4866
4867 def tearDown(self):
a962d857 4868 tear_down_common()
1f0e3109 4869
9540f8e2
YW
4870 def test_bridge_mac_none(self):
4871 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4872 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4873 start_networkd()
95e1fbba 4874 self.wait_online('dummy98:enslaved', 'bridge99:degraded')
9540f8e2
YW
4875
4876 output = check_output('ip link show dev dummy98')
4877 print(output)
4878 self.assertIn('link/ether 12:34:56:78:9a:01', output)
4879
4880 output = check_output('ip link show dev bridge99')
4881 print(output)
4882 self.assertIn('link/ether 12:34:56:78:9a:01', output)
4883
6f943798 4884 def test_bridge_vlan(self):
a962d857 4885 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
60f4b2c5
YW
4886 '26-bridge.netdev', '26-bridge-vlan-master.network',
4887 copy_dropins=False)
6f943798 4888 start_networkd()
95e1fbba 4889 self.wait_online('test1:enslaved', 'bridge99:degraded')
6f943798
YW
4890
4891 output = check_output('bridge vlan show dev test1')
4892 print(output)
60f4b2c5
YW
4893 # check if the default VID is removed
4894 self.assertNotIn('1 Egress Untagged', output)
4895 for i in range(1000, 3000):
4896 if i == 1010:
4897 self.assertIn(f'{i} PVID', output)
4898 elif i in range(1012, 1016) or i in range(1103, 1109):
4899 self.assertIn(f'{i} Egress Untagged', output)
4900 elif i in range(1008, 1014) or i in range(1100, 1111):
4901 self.assertIn(f'{i}', output)
4902 else:
4903 self.assertNotIn(f'{i}', output)
4904
4905 output = check_output('bridge vlan show dev bridge99')
4906 print(output)
4907 # check if the default VID is removed
4908 self.assertNotIn('1 Egress Untagged', output)
4909 for i in range(1000, 3000):
4910 if i == 1020:
4911 self.assertIn(f'{i} PVID', output)
4912 elif i in range(1022, 1026) or i in range(1203, 1209):
4913 self.assertIn(f'{i} Egress Untagged', output)
4914 elif i in range(1018, 1024) or i in range(1200, 1211):
4915 self.assertIn(f'{i}', output)
4916 else:
4917 self.assertNotIn(f'{i}', output)
4918
4919 # Change vlan IDs
4920 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4921 '26-bridge-vlan-master.network.d/10-override.conf')
4922 networkctl_reload()
95e1fbba 4923 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4924
4925 output = check_output('bridge vlan show dev test1')
4926 print(output)
4927 for i in range(1000, 3000):
4928 if i == 2010:
4929 self.assertIn(f'{i} PVID', output)
4930 elif i in range(2012, 2016) or i in range(2103, 2109):
4931 self.assertIn(f'{i} Egress Untagged', output)
4932 elif i in range(2008, 2014) or i in range(2100, 2111):
4933 self.assertIn(f'{i}', output)
4934 else:
4935 self.assertNotIn(f'{i}', output)
4936
4937 output = check_output('bridge vlan show dev bridge99')
4938 print(output)
4939 for i in range(1000, 3000):
4940 if i == 2020:
4941 self.assertIn(f'{i} PVID', output)
4942 elif i in range(2022, 2026) or i in range(2203, 2209):
4943 self.assertIn(f'{i} Egress Untagged', output)
4944 elif i in range(2018, 2024) or i in range(2200, 2211):
4945 self.assertIn(f'{i}', output)
4946 else:
4947 self.assertNotIn(f'{i}', output)
4948
4949 # Remove several vlan IDs
4950 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4951 '26-bridge-vlan-master.network.d/20-override.conf')
4952 networkctl_reload()
95e1fbba 4953 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4954
4955 output = check_output('bridge vlan show dev test1')
4956 print(output)
4957 for i in range(1000, 3000):
4958 if i == 2010:
4959 self.assertIn(f'{i} PVID', output)
4960 elif i in range(2012, 2016):
4961 self.assertIn(f'{i} Egress Untagged', output)
4962 elif i in range(2008, 2014):
4963 self.assertIn(f'{i}', output)
4964 else:
4965 self.assertNotIn(f'{i}', output)
4966
4967 output = check_output('bridge vlan show dev bridge99')
4968 print(output)
4969 for i in range(1000, 3000):
4970 if i == 2020:
4971 self.assertIn(f'{i} PVID', output)
4972 elif i in range(2022, 2026):
4973 self.assertIn(f'{i} Egress Untagged', output)
4974 elif i in range(2018, 2024):
4975 self.assertIn(f'{i}', output)
4976 else:
4977 self.assertNotIn(f'{i}', output)
4978
4979 # Remove all vlan IDs
4980 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4981 '26-bridge-vlan-master.network.d/30-override.conf')
4982 networkctl_reload()
95e1fbba 4983 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4984
4985 output = check_output('bridge vlan show dev test1')
4986 print(output)
4987 self.assertNotIn('PVID', output)
4988 for i in range(1000, 3000):
4989 self.assertNotIn(f'{i}', output)
6f943798
YW
4990
4991 output = check_output('bridge vlan show dev bridge99')
4992 print(output)
60f4b2c5
YW
4993 self.assertNotIn('PVID', output)
4994 for i in range(1000, 3000):
4995 self.assertNotIn(f'{i}', output)
6f943798 4996
988b0660 4997 def test_bridge_vlan_issue_20373(self):
a962d857
YW
4998 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4999 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
5000 '21-vlan.netdev', '21-vlan.network')
988b0660 5001 start_networkd()
95e1fbba 5002 self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
988b0660
YW
5003
5004 output = check_output('bridge vlan show dev test1')
5005 print(output)
5006 self.assertIn('100 PVID Egress Untagged', output)
5007 self.assertIn('560', output)
5008 self.assertIn('600', output)
5009
5010 output = check_output('bridge vlan show dev bridge99')
5011 print(output)
5012 self.assertIn('1 PVID Egress Untagged', output)
5013 self.assertIn('100', output)
5014 self.assertIn('600', output)
5015
cc0276cc 5016 def test_bridge_mdb(self):
a962d857
YW
5017 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
5018 '26-bridge.netdev', '26-bridge-mdb-master.network')
cc0276cc 5019 start_networkd()
95e1fbba 5020 self.wait_online('test1:enslaved', 'bridge99:degraded')
cc0276cc
YW
5021
5022 output = check_output('bridge mdb show dev bridge99')
5023 print(output)
5024 self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
5025 self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
5026
9f773037 5027 # Old kernel may not support bridge MDB entries on bridge master
a962d857 5028 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
9f773037
YW
5029 self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
5030 self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
5031
b06469a6
YW
5032 def test_bridge_keep_master(self):
5033 check_output('ip link add bridge99 type bridge')
5034 check_output('ip link set bridge99 up')
5035 check_output('ip link add dummy98 type dummy')
5036 check_output('ip link set dummy98 master bridge99')
5037
a962d857 5038 copy_network_unit('23-keep-master.network')
b06469a6 5039 start_networkd()
95e1fbba 5040 self.wait_online('dummy98:enslaved')
b06469a6
YW
5041
5042 output = check_output('ip -d link show dummy98')
5043 print(output)
5044 self.assertRegex(output, 'master bridge99')
5045 self.assertRegex(output, 'bridge')
5046
5047 output = check_output('bridge -d link show dummy98')
5048 print(output)
a962d857
YW
5049 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5050 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5051 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5052 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5053 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
b06469a6 5054 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
5055 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
5056 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
5057 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5058 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
5059 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5060 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
b06469a6 5061
1f0e3109 5062 def test_bridge_property(self):
a962d857
YW
5063 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5064 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5065 '25-bridge99.network')
2cf6fdff 5066 start_networkd()
95e1fbba 5067 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
1f0e3109 5068
21d0ed68
YW
5069 output = check_output('ip -d link show bridge99')
5070 print(output)
5071 self.assertIn('mtu 9000 ', output)
5072
371810d1 5073 output = check_output('ip -d link show test1')
1f0e3109 5074 print(output)
21d0ed68
YW
5075 self.assertIn('master bridge99 ', output)
5076 self.assertIn('bridge_slave', output)
5077 self.assertIn('mtu 9000 ', output)
1f0e3109 5078
371810d1 5079 output = check_output('ip -d link show dummy98')
1f0e3109 5080 print(output)
21d0ed68
YW
5081 self.assertIn('master bridge99 ', output)
5082 self.assertIn('bridge_slave', output)
5083 self.assertIn('mtu 9000 ', output)
1f0e3109 5084
371810d1 5085 output = check_output('ip addr show bridge99')
1f0e3109 5086 print(output)
21d0ed68 5087 self.assertIn('192.168.0.15/24', output)
1f0e3109 5088
371810d1 5089 output = check_output('bridge -d link show dummy98')
1f0e3109 5090 print(output)
a962d857
YW
5091 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5092 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5093 self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
5094 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5095 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5096 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5424fd95 5097 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
5098 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
5099 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
5100 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5101 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
5102 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5103 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4d7ed14f 5104
5424fd95
YW
5105 output = check_output('bridge -d link show test1')
5106 print(output)
a962d857 5107 self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
1f0e3109 5108
371810d1 5109 check_output('ip address add 192.168.0.16/24 dev bridge99')
371810d1 5110 output = check_output('ip addr show bridge99')
2be6c5d2 5111 print(output)
21d0ed68 5112 self.assertIn('192.168.0.16/24', output)
2be6c5d2 5113
e3cbaeab
YW
5114 # for issue #6088
5115 print('### ip -6 route list table all dev bridge99')
5116 output = check_output('ip -6 route list table all dev bridge99')
5117 print(output)
beb75dd3 5118 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 5119
a962d857 5120 remove_link('test1')
cf4dbd84 5121 self.wait_operstate('bridge99', 'routable')
2be6c5d2 5122
21d0ed68
YW
5123 output = check_output('ip -d link show bridge99')
5124 print(output)
5125 self.assertIn('mtu 9000 ', output)
5126
5127 output = check_output('ip -d link show dummy98')
5128 print(output)
5129 self.assertIn('master bridge99 ', output)
5130 self.assertIn('bridge_slave', output)
5131 self.assertIn('mtu 9000 ', output)
804b6cd2 5132
21d0ed68 5133 remove_link('dummy98')
19cf3143 5134 self.wait_operstate('bridge99', 'no-carrier')
2be6c5d2 5135
21d0ed68
YW
5136 output = check_output('ip -d link show bridge99')
5137 print(output)
5138 # When no carrier, the kernel may reset the MTU
5139 self.assertIn('NO-CARRIER', output)
5140
371810d1 5141 output = check_output('ip address show bridge99')
804b6cd2 5142 print(output)
21d0ed68
YW
5143 self.assertNotIn('192.168.0.15/24', output)
5144 self.assertIn('192.168.0.16/24', output) # foreign address is kept
804b6cd2 5145
e3cbaeab
YW
5146 print('### ip -6 route list table all dev bridge99')
5147 output = check_output('ip -6 route list table all dev bridge99')
5148 print(output)
beb75dd3 5149 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 5150
21d0ed68 5151 check_output('ip link add dummy98 type dummy')
95e1fbba 5152 self.wait_online('dummy98:enslaved', 'bridge99:routable')
21d0ed68
YW
5153
5154 output = check_output('ip -d link show bridge99')
5155 print(output)
5156 self.assertIn('mtu 9000 ', output)
5157
5158 output = check_output('ip -d link show dummy98')
5159 print(output)
5160 self.assertIn('master bridge99 ', output)
5161 self.assertIn('bridge_slave', output)
5162 self.assertIn('mtu 9000 ', output)
5163
0fc0d85f 5164 def test_bridge_configure_without_carrier(self):
a962d857
YW
5165 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
5166 '11-dummy.netdev')
0fc0d85f
DS
5167 start_networkd()
5168
5169 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5170 for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
5171 with self.subTest(test=test):
5172 if test == 'no-slave':
5173 # bridge has no slaves; it's up but *might* not have carrier
001c07cf 5174 self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
0fc0d85f
DS
5175 # due to a bug in the kernel, newly-created bridges are brought up
5176 # *with* carrier, unless they have had any setting changed; e.g.
5177 # their mac set, priority set, etc. Then, they will lose carrier
5178 # as soon as a (down) slave interface is added, and regain carrier
5179 # again once the slave interface is brought up.
5180 #self.check_link_attr('bridge99', 'carrier', '0')
5181 elif test == 'add-slave':
5182 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5183 self.check_link_attr('test1', 'operstate', 'down')
5184 check_output('ip link set dev test1 master bridge99')
001c07cf 5185 self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
0fc0d85f
DS
5186 self.check_link_attr('bridge99', 'carrier', '0')
5187 elif test == 'slave-up':
5188 # bring up slave, which will have carrier; bridge gains carrier
5189 check_output('ip link set dev test1 up')
95e1fbba 5190 self.wait_online('bridge99:routable')
0fc0d85f
DS
5191 self.check_link_attr('bridge99', 'carrier', '1')
5192 elif test == 'slave-no-carrier':
5193 # drop slave carrier; bridge loses carrier
5194 check_output('ip link set dev test1 carrier off')
95e1fbba 5195 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
5196 self.check_link_attr('bridge99', 'carrier', '0')
5197 elif test == 'slave-carrier':
5198 # restore slave carrier; bridge gains carrier
5199 check_output('ip link set dev test1 carrier on')
95e1fbba 5200 self.wait_online('bridge99:routable')
0fc0d85f
DS
5201 self.check_link_attr('bridge99', 'carrier', '1')
5202 elif test == 'slave-down':
5203 # bring down slave; bridge loses carrier
5204 check_output('ip link set dev test1 down')
95e1fbba 5205 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
5206 self.check_link_attr('bridge99', 'carrier', '0')
5207
10d670a3 5208 output = networkctl_status('bridge99')
0fc0d85f
DS
5209 self.assertRegex(output, '10.1.2.3')
5210 self.assertRegex(output, '10.1.2.1')
5211
804b6cd2 5212 def test_bridge_ignore_carrier_loss(self):
a962d857
YW
5213 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5214 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5215 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 5216 start_networkd()
95e1fbba 5217 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
804b6cd2 5218
371810d1 5219 check_output('ip address add 192.168.0.16/24 dev bridge99')
a962d857 5220 remove_link('test1', 'dummy98')
804b6cd2
YW
5221 time.sleep(3)
5222
371810d1 5223 output = check_output('ip address show bridge99')
804b6cd2
YW
5224 print(output)
5225 self.assertRegex(output, 'NO-CARRIER')
5226 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5227 self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
5228
6609924c 5229 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
a962d857
YW
5230 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5231 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 5232 start_networkd()
95e1fbba 5233 self.wait_online('bridge99:no-carrier')
6609924c 5234
90e3bcbd
YW
5235 for trial in range(4):
5236 check_output('ip link add dummy98 type dummy')
5237 check_output('ip link set dummy98 up')
5238 if trial < 3:
a962d857 5239 remove_link('dummy98')
6609924c 5240
95e1fbba 5241 self.wait_online('bridge99:routable', 'dummy98:enslaved')
6609924c 5242
371810d1 5243 output = check_output('ip address show bridge99')
6609924c
YW
5244 print(output)
5245 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5246
371810d1 5247 output = check_output('ip rule list table 100')
6609924c 5248 print(output)
c4f7a347 5249 self.assertIn('from all to 8.8.8.8 lookup 100', output)
6609924c 5250
a03ff4c0 5251class NetworkdSRIOVTests(unittest.TestCase, Utilities):
a03ff4c0
YW
5252
5253 def setUp(self):
a962d857 5254 setup_common()
a03ff4c0
YW
5255
5256 def tearDown(self):
a962d857 5257 tear_down_common()
a03ff4c0
YW
5258
5259 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5260 def test_sriov(self):
d8746f16
YW
5261 copy_network_unit('25-default.link', '25-sriov.network')
5262
a962d857 5263 call('modprobe netdevsim')
a03ff4c0 5264
d45476ef 5265 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
a03ff4c0
YW
5266 f.write('99 1')
5267
a962d857 5268 with open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode='w', encoding='utf-8') as f:
a03ff4c0
YW
5269 f.write('3')
5270
a03ff4c0 5271 start_networkd()
95e1fbba 5272 self.wait_online('eni99np1:routable')
a03ff4c0
YW
5273
5274 output = check_output('ip link show dev eni99np1')
5275 print(output)
5276 self.assertRegex(output,
5277 '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 *'
5278 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5279 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5280 )
a03ff4c0 5281
1e8e9730
YW
5282 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5283 def test_sriov_udev(self):
a962d857 5284 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
1e8e9730 5285
a962d857
YW
5286 call('modprobe netdevsim')
5287
d45476ef 5288 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
1e8e9730
YW
5289 f.write('99 1')
5290
5291 start_networkd()
95e1fbba 5292 self.wait_online('eni99np1:routable')
1e8e9730 5293
b95d35b5
YW
5294 # the name eni99np1 may be an alternative name.
5295 ifname = link_resolve('eni99np1')
5296
1e8e9730
YW
5297 output = check_output('ip link show dev eni99np1')
5298 print(output)
5299 self.assertRegex(output,
5300 '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 *'
5301 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5302 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5303 )
1e8e9730
YW
5304 self.assertNotIn('vf 3', output)
5305 self.assertNotIn('vf 4', output)
5306
a962d857 5307 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5308 f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5309
a39a2a81
YW
5310 udevadm_reload()
5311 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5312
5313 output = check_output('ip link show dev eni99np1')
5314 print(output)
5315 self.assertRegex(output,
5316 '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 *'
5317 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5318 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5319 'vf 3'
a962d857 5320 )
1e8e9730
YW
5321 self.assertNotIn('vf 4', output)
5322
a962d857 5323 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5324 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
5325
a39a2a81
YW
5326 udevadm_reload()
5327 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5328
5329 output = check_output('ip link show dev eni99np1')
5330 print(output)
5331 self.assertRegex(output,
5332 '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 *'
5333 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5334 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5335 'vf 3'
a962d857 5336 )
1e8e9730
YW
5337 self.assertNotIn('vf 4', output)
5338
a962d857 5339 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5340 f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5341
a39a2a81
YW
5342 udevadm_reload()
5343 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5344
5345 output = check_output('ip link show dev eni99np1')
5346 print(output)
5347 self.assertRegex(output,
5348 '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 *'
5349 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
a962d857 5350 )
1e8e9730
YW
5351 self.assertNotIn('vf 2', output)
5352 self.assertNotIn('vf 3', output)
5353 self.assertNotIn('vf 4', output)
5354
a962d857 5355 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5356 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
5357
a39a2a81
YW
5358 udevadm_reload()
5359 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5360
5361 output = check_output('ip link show dev eni99np1')
5362 print(output)
5363 self.assertRegex(output,
5364 '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 *'
5365 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5366 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5367 )
1e8e9730
YW
5368 self.assertNotIn('vf 3', output)
5369 self.assertNotIn('vf 4', output)
5370
be68c2c9 5371class NetworkdLLDPTests(unittest.TestCase, Utilities):
1f0e3109
SS
5372
5373 def setUp(self):
a962d857 5374 setup_common()
1f0e3109
SS
5375
5376 def tearDown(self):
a962d857 5377 tear_down_common()
1f0e3109
SS
5378
5379 def test_lldp(self):
a962d857 5380 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
2cf6fdff 5381 start_networkd()
95e1fbba 5382 self.wait_online('veth99:degraded', 'veth-peer:degraded')
1f0e3109 5383
f0d87798
YW
5384 for trial in range(10):
5385 if trial > 0:
5386 time.sleep(1)
5387
10d670a3 5388 output = networkctl('lldp')
f0d87798 5389 print(output)
0f805b46 5390 if re.search(r'veth99 .* veth-peer .* .......a...', output):
f0d87798
YW
5391 break
5392 else:
5393 self.fail()
1f0e3109 5394
d6360819
YW
5395 # With interface name
5396 output = networkctl('lldp', 'veth99');
5397 print(output)
0f805b46 5398 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
5399
5400 # With interface name pattern
5401 output = networkctl('lldp', 've*9');
5402 print(output)
0f805b46 5403 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
5404
5405 # json format
5406 output = networkctl('--json=short', 'lldp')
5407 print(output)
5408 self.assertIn('"InterfaceName":"veth99"', output)
5409 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 5410 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
5411
5412 # json format with interface name
5413 output = networkctl('--json=short', 'lldp', 'veth99')
5414 print(output)
5415 self.assertIn('"InterfaceName":"veth99"', output)
5416 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 5417 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
5418
5419 # json format with interface name pattern
5420 output = networkctl('--json=short', 'lldp', 've*9')
5421 print(output)
5422 self.assertIn('"InterfaceName":"veth99"', output)
5423 self.assertIn('"PortID":"veth-peer"', output)
0f805b46
YW
5424 self.assertIn('"EnabledCapabilities":128', output)
5425
5426 # LLDP neighbors in status
5427 output = networkctl_status('veth99')
5428 print(output)
5429 self.assertRegex(output, r'Connected To: .* on port veth-peer')
5430
5431 # enable forwarding, to enable the Router flag
5432 with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
5433 f.write('[Network]\nIPv4Forwarding=yes\n')
5434
5435 networkctl_reload()
5436 self.wait_online('veth-peer:degraded')
5437
5438 for trial in range(10):
5439 if trial > 0:
5440 time.sleep(1)
5441
5442 output = networkctl('lldp')
5443 print(output)
5444 if re.search(r'veth99 .* veth-peer .* ....r......', output):
5445 break
5446 else:
5447 self.fail()
5448
5449 # With interface name
5450 output = networkctl('lldp', 'veth99');
5451 print(output)
5452 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
5453
5454 # With interface name pattern
5455 output = networkctl('lldp', 've*9');
5456 print(output)
5457 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
5458
5459 # json format
5460 output = networkctl('--json=short', 'lldp')
5461 print(output)
5462 self.assertIn('"InterfaceName":"veth99"', output)
5463 self.assertIn('"PortID":"veth-peer"', output)
5464 self.assertIn('"EnabledCapabilities":16', output)
5465
5466 # json format with interface name
5467 output = networkctl('--json=short', 'lldp', 'veth99')
5468 print(output)
5469 self.assertIn('"InterfaceName":"veth99"', output)
5470 self.assertIn('"PortID":"veth-peer"', output)
5471 self.assertIn('"EnabledCapabilities":16', output)
5472
5473 # json format with interface name pattern
5474 output = networkctl('--json=short', 'lldp', 've*9')
5475 print(output)
5476 self.assertIn('"InterfaceName":"veth99"', output)
5477 self.assertIn('"PortID":"veth-peer"', output)
5478 self.assertIn('"EnabledCapabilities":16', output)
d6360819
YW
5479
5480 # LLDP neighbors in status
5481 output = networkctl_status('veth99')
5482 print(output)
5483 self.assertRegex(output, r'Connected To: .* on port veth-peer')
5484
be68c2c9 5485class NetworkdRATests(unittest.TestCase, Utilities):
1f0e3109
SS
5486
5487 def setUp(self):
a962d857 5488 setup_common()
1f0e3109
SS
5489
5490 def tearDown(self):
a962d857 5491 tear_down_common()
1f0e3109
SS
5492
5493 def test_ipv6_prefix_delegation(self):
a962d857 5494 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
c742d7e8
TM
5495 self.setup_nftset('addr6', 'ipv6_addr')
5496 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5497 self.setup_nftset('ifindex', 'iface_index')
2cf6fdff 5498 start_networkd()
95e1fbba 5499 self.wait_online('veth99:routable', 'veth-peer:degraded')
1f0e3109 5500
3976c430
YW
5501 # IPv6SendRA=yes implies IPv6Forwarding.
5502 self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5503
10d670a3 5504 output = resolvectl('dns', 'veth99')
41fd8fe7
YW
5505 print(output)
5506 self.assertRegex(output, 'fe80::')
5507 self.assertRegex(output, '2002:da8:1::1')
5508
10d670a3 5509 output = resolvectl('domain', 'veth99')
80762ccc
YW
5510 print(output)
5511 self.assertIn('hogehoge.test', output)
5512
10d670a3 5513 output = networkctl_status('veth99')
1f0e3109
SS
5514 print(output)
5515 self.assertRegex(output, '2002:da8:1:0')
5516
0fe4a1c8
YW
5517 self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
5518 self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
5519
a4640bed
TM
5520 self.check_netlabel('veth99', '2002:da8:1::/64')
5521 self.check_netlabel('veth99', '2002:da8:2::/64')
5522
c742d7e8
TM
5523 self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5524 self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5525 self.check_nftset('network6', '2002:da8:1::/64')
5526 self.check_nftset('network6', '2002:da8:2::/64')
5527 self.check_nftset('ifindex', 'veth99')
5528
5529 self.teardown_nftset('addr6', 'network6', 'ifindex')
5530
6d1cea7b 5531 def check_ipv6_token_static(self):
95e1fbba 5532 self.wait_online('veth99:routable', 'veth-peer:degraded')
b241fa00 5533
10d670a3 5534 output = networkctl_status('veth99')
b241fa00
KF
5535 print(output)
5536 self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
68248f43
YW
5537 self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
5538 self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
5539 self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
b241fa00 5540
6d1cea7b
YW
5541 def test_ipv6_token_static(self):
5542 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5543 start_networkd()
5544
5545 self.check_ipv6_token_static()
5546
5547 for _ in range(20):
5548 check_output('ip link set veth99 down')
5549 check_output('ip link set veth99 up')
5550
5551 self.check_ipv6_token_static()
5552
5553 for _ in range(20):
5554 check_output('ip link set veth99 down')
5555 time.sleep(random.uniform(0, 0.1))
5556 check_output('ip link set veth99 up')
5557 time.sleep(random.uniform(0, 0.1))
5558
5559 self.check_ipv6_token_static()
5560
9dcdf16b
YW
5561 def test_ndisc_redirect(self):
5562 if not os.path.exists(test_ndisc_send):
5563 self.skipTest(f"{test_ndisc_send} does not exist.")
5564
5565 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5566 start_networkd()
5567
5568 self.check_ipv6_token_static()
5569
5570 # Introduce two redirect routes.
5571 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
9944629e
YW
5572 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5573 self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5574 self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
9dcdf16b
YW
5575
5576 # Change the target address of the redirects.
9944629e
YW
5577 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
5578 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5579 self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5580 self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5581 self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
5582 self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
9dcdf16b
YW
5583
5584 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
5585 # Then, verify that all redirect routes and the default route are dropped.
5586 output = check_output('ip -6 address show dev veth-peer scope link')
5587 veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
5588 print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
5589 check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
9dcdf16b
YW
5590 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
5591
9944629e
YW
5592 output = check_output('ip -6 route show dev veth99')
5593 print(output)
5594 self.assertIn('2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', output)
5595 self.assertIn('2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', output)
5596
3b4eeccd
YW
5597 # Check if sd-radv refuses RS from the same interface.
5598 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
5599 since = datetime.datetime.now()
5600 check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
5601 self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
5602
a0430b0d
YW
5603 def check_ndisc_mtu(self, mtu):
5604 for _ in range(20):
5605 output = read_ipv6_sysctl_attr('veth99', 'mtu')
5606 if output == f'{mtu}':
5607 break
5608 time.sleep(0.5)
5609 else:
5610 self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
5611
5612 def test_ndisc_mtu(self):
5613 if not os.path.exists(test_ndisc_send):
5614 self.skipTest(f"{test_ndisc_send} does not exist.")
5615
5616 copy_network_unit('25-veth.netdev',
5617 '25-veth-peer-no-address.network',
5618 '25-ipv6-prefix-veth-token-static.network')
5619 start_networkd()
5620 self.wait_online('veth-peer:degraded')
5621
45c2bbba 5622 self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
a0430b0d
YW
5623
5624 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
5625 self.check_ndisc_mtu(1400)
5626
5627 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
5628 self.check_ndisc_mtu(1410)
5629
5630 check_output('ip link set dev veth99 mtu 1600')
5631 self.check_ndisc_mtu(1410)
5632
5633 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
5634 self.check_ndisc_mtu(1600)
5635
5636 check_output('ip link set dev veth99 mtu 1800')
5637 self.check_ndisc_mtu(1700)
5638
68248f43 5639 def test_ipv6_token_prefixstable(self):
a962d857 5640 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
c24c83dc 5641 start_networkd()
95e1fbba 5642 self.wait_online('veth99:routable', 'veth-peer:degraded')
c24c83dc 5643
ce4ed0ad 5644 output = check_output('ip -6 address show dev veth99')
c24c83dc 5645 print(output)
ce4ed0ad
YW
5646 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5647 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
5648
5649 with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
5650 f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
5651
5652 networkctl_reload()
5653 self.wait_online('veth99:routable')
5654
5655 output = check_output('ip -6 address show dev veth99')
5656 print(output)
5657 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5658 self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
5659
5660 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
5661 check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
5662
5663 networkctl_reconfigure('veth99')
5664 self.wait_online('veth99:routable')
5665
5666 output = check_output('ip -6 address show dev veth99')
5667 print(output)
5668 self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5669 self.assertIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
5670
5671 check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
5672 check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
5673
5674 networkctl_reconfigure('veth99')
5675 self.wait_online('veth99:routable')
5676
5677 output = check_output('ip -6 address show dev veth99')
5678 print(output)
5679 self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5680 self.assertNotIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
5681 self.assertIn('2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', output) # the 3rd prefixstable
c24c83dc 5682
68248f43 5683 def test_ipv6_token_prefixstable_without_address(self):
a962d857 5684 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
87bbebea 5685 start_networkd()
95e1fbba 5686 self.wait_online('veth99:routable', 'veth-peer:degraded')
87bbebea 5687
10d670a3 5688 output = networkctl_status('veth99')
87bbebea 5689 print(output)
7a2e124b
YW
5690 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
5691 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
87bbebea 5692
29fbbb13
YW
5693 def test_router_hop_limit(self):
5694 copy_network_unit('25-veth-client.netdev',
5695 '25-veth-router.netdev',
5696 '26-bridge.netdev',
5697 '25-veth-bridge.network',
5698 '25-veth-client.network',
5699 '25-veth-router-hop-limit.network',
5700 '25-bridge99.network')
5701 start_networkd()
2e73aa50 5702 self.wait_online('client:routable', 'client-p:enslaved',
29fbbb13
YW
5703 'router:degraded', 'router-p:enslaved',
5704 'bridge99:routable')
5705
2e73aa50 5706 self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
29fbbb13
YW
5707
5708 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
5709 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
5710
5711 networkctl_reload()
5712
2e73aa50
YW
5713 for _ in range(20):
5714 output = read_ipv6_sysctl_attr('client', 'hop_limit')
5715 if output == '43':
5716 break
5717 time.sleep(0.5)
5718
5719 self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
29fbbb13 5720
bb80f633
YW
5721 def test_router_preference(self):
5722 copy_network_unit('25-veth-client.netdev',
5723 '25-veth-router-high.netdev',
5724 '25-veth-router-low.netdev',
5725 '26-bridge.netdev',
5726 '25-veth-bridge.network',
5727 '25-veth-client.network',
5728 '25-veth-router-high.network',
5729 '25-veth-router-low.network',
5730 '25-bridge99.network')
5731 start_networkd()
95e1fbba
YW
5732 self.wait_online('client-p:enslaved',
5733 'router-high:degraded', 'router-high-p:enslaved',
5734 'router-low:degraded', 'router-low-p:enslaved',
5735 'bridge99:routable')
bb80f633
YW
5736
5737 networkctl_reconfigure('client')
95e1fbba 5738 self.wait_online('client:routable')
bb80f633
YW
5739
5740 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5741 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5742 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv='-6', timeout_sec=10)
5743 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv='-6', timeout_sec=10)
5744
5745 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5746 print(output)
9fbab82b 5747 self.assertIn('metric 512', output)
bb80f633
YW
5748 self.assertIn('pref high', output)
5749 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5750 print(output)
9fbab82b 5751 self.assertIn('metric 2048', output)
bb80f633
YW
5752 self.assertIn('pref low', output)
5753
5754 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
5755 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5756
5757 networkctl_reload()
95e1fbba 5758 self.wait_online('client:routable')
bb80f633
YW
5759
5760 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5761 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5762 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv='-6', timeout_sec=10)
5763 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv='-6', timeout_sec=10)
5764
5765 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5766 print(output)
9fbab82b
YW
5767 self.assertIn('metric 100', output)
5768 self.assertNotIn('metric 512', output)
bb80f633
YW
5769 self.assertIn('pref high', output)
5770 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5771 print(output)
9fbab82b
YW
5772 self.assertIn('metric 300', output)
5773 self.assertNotIn('metric 2048', output)
bb80f633
YW
5774 self.assertIn('pref low', output)
5775
9fbab82b
YW
5776 # swap the preference (for issue #28439)
5777 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5778 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5779 networkctl_reload()
5780 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv='-6', timeout_sec=10)
5781 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv='-6', timeout_sec=10)
5782
5783 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5784 print(output)
5785 self.assertIn('metric 300', output)
5786 self.assertNotIn('metric 100', output)
5787 self.assertIn('pref low', output)
5788 self.assertNotIn('pref high', output)
5789 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5790 print(output)
5791 self.assertIn('metric 100', output)
5792 self.assertNotIn('metric 300', output)
5793 self.assertIn('pref high', output)
5794 self.assertNotIn('pref low', output)
5795
c1dd58b3
FS
5796 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5797 def test_captive_portal(self):
5798 copy_network_unit('25-veth-client.netdev',
5799 '25-veth-router-captive.netdev',
5800 '26-bridge.netdev',
5801 '25-veth-client-captive.network',
5802 '25-veth-router-captive.network',
5803 '25-veth-bridge-captive.network',
5804 '25-bridge99.network')
5805 start_networkd()
95e1fbba
YW
5806 self.wait_online('bridge99:routable', 'client-p:enslaved',
5807 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
5808
5809 start_radvd(config_file='captive-portal.conf')
5810 networkctl_reconfigure('client')
95e1fbba 5811 self.wait_online('client:routable')
c1dd58b3
FS
5812
5813 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 5814 output = networkctl_status('client')
c1dd58b3
FS
5815 print(output)
5816 self.assertIn('Captive Portal: http://systemd.io', output)
5817
5818 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5819 def test_invalid_captive_portal(self):
5820 def radvd_write_config(captive_portal_uri):
5821 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
5822 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5823
5824 captive_portal_uris = [
5825 "42ěščěškd ěšč ě s",
5826 " ",
5827 "🤔",
5828 ]
5829
5830 copy_network_unit('25-veth-client.netdev',
5831 '25-veth-router-captive.netdev',
5832 '26-bridge.netdev',
5833 '25-veth-client-captive.network',
5834 '25-veth-router-captive.network',
5835 '25-veth-bridge-captive.network',
5836 '25-bridge99.network')
5837 start_networkd()
95e1fbba
YW
5838 self.wait_online('bridge99:routable', 'client-p:enslaved',
5839 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
5840
5841 for uri in captive_portal_uris:
5842 print(f"Captive portal: {uri}")
5843 radvd_write_config(uri)
5844 stop_radvd()
5845 start_radvd(config_file='bogus-captive-portal.conf')
5846 networkctl_reconfigure('client')
95e1fbba 5847 self.wait_online('client:routable')
c1dd58b3
FS
5848
5849 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 5850 output = networkctl_status('client')
c1dd58b3
FS
5851 print(output)
5852 self.assertNotIn('Captive Portal:', output)
5853
be68c2c9 5854class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
1f0e3109
SS
5855
5856 def setUp(self):
a962d857 5857 setup_common()
1f0e3109
SS
5858
5859 def tearDown(self):
a962d857 5860 tear_down_common()
1f0e3109 5861
bc91875a 5862 def check_dhcp_server(self, persist_leases=True):
10d670a3 5863 output = networkctl_status('veth99')
c5f7a087 5864 print(output)
e6c4b5dc 5865 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
c5f7a087
YW
5866 self.assertIn('Gateway: 192.168.5.3', output)
5867 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
5868 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
5869
10d670a3 5870 output = networkctl_status('veth-peer')
1c4411b7 5871 print(output)
8bdece74
FS
5872 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
5873
bc91875a
YW
5874 if persist_leases:
5875 with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
5876 check_json(f.read())
5877 else:
5878 self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
5879
5880 def test_dhcp_server(self):
5881 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5882 start_networkd()
5883 self.wait_online('veth99:routable', 'veth-peer:routable')
5884
5885 self.check_dhcp_server()
5886
1c4411b7
YW
5887 networkctl_reconfigure('veth-peer')
5888 self.wait_online('veth-peer:routable')
5889
5890 for _ in range(10):
5891 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
5892 if 'Offered DHCP leases: 192.168.5.' in output:
5893 break
5894 time.sleep(.2)
5895 else:
5896 self.fail()
5897
bc91875a
YW
5898 def test_dhcp_server_persist_leases_no(self):
5899 copy_networkd_conf_dropin('persist-leases-no.conf')
5900 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5901 start_networkd()
5902 self.wait_online('veth99:routable', 'veth-peer:routable')
5903
5904 self.check_dhcp_server(persist_leases=False)
5905
5906 remove_networkd_conf_dropin('persist-leases-no.conf')
5907 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
5908 f.write('[DHCPServer]\nPersistLeases=no')
5909 restart_networkd()
5910 self.wait_online('veth99:routable', 'veth-peer:routable')
5911
5912 self.check_dhcp_server(persist_leases=False)
5913
47f1ce16
YW
5914 def test_dhcp_server_null_server_address(self):
5915 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5916 start_networkd()
95e1fbba 5917 self.wait_online('veth99:routable', 'veth-peer:routable')
47f1ce16
YW
5918
5919 output = check_output('ip --json address show dev veth-peer')
5920 server_address = json.loads(output)[0]['addr_info'][0]['local']
5921 print(server_address)
5922
5923 output = check_output('ip --json address show dev veth99')
5924 client_address = json.loads(output)[0]['addr_info'][0]['local']
5925 print(client_address)
5926
10d670a3 5927 output = networkctl_status('veth99')
47f1ce16 5928 print(output)
e6c4b5dc 5929 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
47f1ce16
YW
5930 self.assertIn(f'Gateway: {server_address}', output)
5931 self.assertIn(f'DNS: {server_address}', output)
5932 self.assertIn(f'NTP: {server_address}', output)
5933
10d670a3 5934 output = networkctl_status('veth-peer')
47f1ce16
YW
5935 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5936
93126bb0
YW
5937 # Check if the same addresses are used even if the service is restarted.
5938 restart_networkd()
5939 self.wait_online('veth99:routable', 'veth-peer:routable')
5940
5941 output = check_output('ip -4 address show dev veth-peer')
5942 print(output)
5943 self.assertIn(f'{server_address}', output)
5944
5945 output = check_output('ip -4 address show dev veth99')
5946 print(output)
5947 self.assertIn(f'{client_address}', output)
5948
5949 output = networkctl_status('veth99')
5950 print(output)
e6c4b5dc 5951 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
93126bb0
YW
5952 self.assertIn(f'Gateway: {server_address}', output)
5953 self.assertIn(f'DNS: {server_address}', output)
5954 self.assertIn(f'NTP: {server_address}', output)
5955
5956 output = networkctl_status('veth-peer')
5957 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5958
c5f7a087 5959 def test_dhcp_server_with_uplink(self):
a962d857
YW
5960 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5961 '12-dummy.netdev', '25-dhcp-server-uplink.network')
2cf6fdff 5962 start_networkd()
95e1fbba 5963 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 5964
10d670a3 5965 output = networkctl_status('veth99')
1f0e3109 5966 print(output)
e6c4b5dc 5967 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
32d97330
YW
5968 self.assertIn('Gateway: 192.168.5.3', output)
5969 self.assertIn('DNS: 192.168.5.1', output)
5970 self.assertIn('NTP: 192.168.5.1', output)
1f0e3109 5971
1f0e3109 5972 def test_emit_router_timezone(self):
a962d857 5973 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
2cf6fdff 5974 start_networkd()
95e1fbba 5975 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 5976
10d670a3 5977 output = networkctl_status('veth99')
1f0e3109 5978 print(output)
e6c4b5dc 5979 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
32d97330
YW
5980 self.assertIn('Gateway: 192.168.5.1', output)
5981 self.assertIn('Time Zone: Europe/Berlin', output)
1f0e3109 5982
ffaece68 5983 def test_dhcp_server_static_lease(self):
a962d857 5984 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
ffaece68 5985 start_networkd()
95e1fbba 5986 self.wait_online('veth99:routable', 'veth-peer:routable')
ffaece68 5987
10d670a3 5988 output = networkctl_status('veth99')
ffaece68 5989 print(output)
e6c4b5dc
SP
5990 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
5991 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
82c60c93
YW
5992
5993 def test_dhcp_server_static_lease_default_client_id(self):
5994 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5995 start_networkd()
95e1fbba 5996 self.wait_online('veth99:routable', 'veth-peer:routable')
82c60c93 5997
10d670a3 5998 output = networkctl_status('veth99')
82c60c93 5999 print(output)
e6c4b5dc
SP
6000 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
6001 self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
ffaece68 6002
c95df587 6003class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
c95df587
YA
6004
6005 def setUp(self):
a962d857 6006 setup_common()
c95df587
YA
6007
6008 def tearDown(self):
a962d857 6009 tear_down_common()
c95df587
YA
6010
6011 def test_relay_agent(self):
a962d857
YW
6012 copy_network_unit('25-agent-veth-client.netdev',
6013 '25-agent-veth-server.netdev',
6014 '25-agent-client.network',
6015 '25-agent-server.network',
6016 '25-agent-client-peer.network',
6017 '25-agent-server-peer.network')
c95df587
YA
6018 start_networkd()
6019
95e1fbba 6020 self.wait_online('client:routable')
c95df587 6021
10d670a3 6022 output = networkctl_status('client')
c95df587 6023 print(output)
e6c4b5dc 6024 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)')
c95df587 6025
c4047829 6026 def test_relay_agent_on_bridge(self):
a663ddc0
YW
6027 copy_network_unit('25-agent-bridge.netdev',
6028 '25-agent-veth-client.netdev',
6029 '25-agent-bridge.network',
6030 '25-agent-bridge-port.network',
6031 '25-agent-client.network')
6032 start_networkd()
95e1fbba 6033 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
a663ddc0
YW
6034
6035 # For issue #30763.
45c2bbba 6036 self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
a663ddc0 6037
be68c2c9 6038class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
1f0e3109
SS
6039
6040 def setUp(self):
a962d857 6041 setup_common()
1f0e3109
SS
6042
6043 def tearDown(self):
a962d857 6044 tear_down_common()
1f0e3109
SS
6045
6046 def test_dhcp_client_ipv6_only(self):
a962d857 6047 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
1f0e3109 6048
2cf6fdff 6049 start_networkd()
95e1fbba 6050 self.wait_online('veth-peer:carrier')
2d7ca6b4
YW
6051
6052 # information request mode
46f2eb51
YW
6053 # The name ipv6-only option may not be supported by older dnsmasq
6054 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
6055 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6056 '--dhcp-option=option6:dns-server,[2600::ee]',
2d7ca6b4
YW
6057 '--dhcp-option=option6:ntp-server,[2600::ff]',
6058 ra_mode='ra-stateless')
95e1fbba 6059 self.wait_online('veth99:routable', 'veth-peer:routable')
2d7ca6b4 6060
0f9efffa
YW
6061 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
6062 # Let's wait for the expected DNS server being listed in the state file.
6063 for _ in range(100):
6064 output = read_link_state_file('veth99')
0f9efffa
YW
6065 if 'DNS=2600::ee' in output:
6066 break
6067 time.sleep(.2)
6068
2d7ca6b4
YW
6069 # Check link state file
6070 print('## link state file')
6071 output = read_link_state_file('veth99')
6072 print(output)
6073 self.assertIn('DNS=2600::ee', output)
6074 self.assertIn('NTP=2600::ff', output)
6075
6076 # Check manager state file
6077 print('## manager state file')
6078 output = read_manager_state_file()
6079 print(output)
6080 self.assertRegex(output, 'DNS=.*2600::ee')
6081 self.assertRegex(output, 'NTP=.*2600::ff')
6082
6083 print('## dnsmasq log')
6084 output = read_dnsmasq_log_file()
6085 print(output)
6086 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
6087 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
6088 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6089 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6090 self.assertNotIn('DHCPREPLY(veth-peer)', output)
6091
3aa47694 6092 # Check json format
10d670a3 6093 check_json(networkctl_json('veth99'))
3aa47694 6094
2d7ca6b4
YW
6095 # solicit mode
6096 stop_dnsmasq()
46f2eb51
YW
6097 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6098 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 6099 '--dhcp-option=option6:ntp-server,[2600::ff]')
2d7ca6b4 6100 networkctl_reconfigure('veth99')
95e1fbba 6101 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 6102
18f2638f
YW
6103 # checking address
6104 output = check_output('ip address show dev veth99 scope global')
c5fcd8a7 6105 print(output)
18f2638f
YW
6106 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6107 self.assertNotIn('192.168.5', output)
c5fcd8a7 6108
589af70b
YW
6109 # checking semi-static route
6110 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6111 print(output)
6112 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6113
3a956d38 6114 # Confirm that ipv6 token is not set in the kernel
371810d1 6115 output = check_output('ip token show dev veth99')
3a956d38
YW
6116 print(output)
6117 self.assertRegex(output, 'token :: dev veth99')
6118
0f9efffa 6119 # Make manager and link state file updated
10d670a3 6120 resolvectl('revert', 'veth99')
0f9efffa 6121
34290c6a
YW
6122 # Check link state file
6123 print('## link state file')
6124 output = read_link_state_file('veth99')
6125 print(output)
6126 self.assertIn('DNS=2600::ee', output)
6127 self.assertIn('NTP=2600::ff', output)
6128
6129 # Check manager state file
6130 print('## manager state file')
6131 output = read_manager_state_file()
6132 print(output)
6133 self.assertRegex(output, 'DNS=.*2600::ee')
6134 self.assertRegex(output, 'NTP=.*2600::ff')
6135
91a7afde
YW
6136 print('## dnsmasq log')
6137 output = read_dnsmasq_log_file()
6138 print(output)
2d7ca6b4 6139 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
6140 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6141 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6142 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6143 self.assertIn('DHCPREPLY(veth-peer)', output)
6144 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6145
3aa47694 6146 # Check json format
10d670a3 6147 check_json(networkctl_json('veth99'))
3aa47694
YW
6148
6149 # Testing without rapid commit support
91a7afde
YW
6150 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
6151 f.write('\n[DHCPv6]\nRapidCommit=no\n')
6152
6153 stop_dnsmasq()
46f2eb51
YW
6154 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6155 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 6156 '--dhcp-option=option6:ntp-server,[2600::ff]')
91a7afde
YW
6157
6158 networkctl_reload()
95e1fbba 6159 self.wait_online('veth99:routable', 'veth-peer:routable')
91a7afde
YW
6160
6161 # checking address
6162 output = check_output('ip address show dev veth99 scope global')
6163 print(output)
6164 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6165 self.assertNotIn('192.168.5', output)
6166
6167 # checking semi-static route
6168 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6169 print(output)
6170 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6171
0f9efffa 6172 # Make manager and link state file updated
10d670a3 6173 resolvectl('revert', 'veth99')
0f9efffa 6174
34290c6a
YW
6175 # Check link state file
6176 print('## link state file')
6177 output = read_link_state_file('veth99')
6178 print(output)
6179 self.assertIn('DNS=2600::ee', output)
6180 self.assertIn('NTP=2600::ff', output)
6181
6182 # Check manager state file
6183 print('## manager state file')
6184 output = read_manager_state_file()
6185 print(output)
6186 self.assertRegex(output, 'DNS=.*2600::ee')
6187 self.assertRegex(output, 'NTP=.*2600::ff')
6188
91a7afde
YW
6189 print('## dnsmasq log')
6190 output = read_dnsmasq_log_file()
6191 print(output)
2d7ca6b4 6192 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
6193 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6194 self.assertIn('DHCPADVERTISE(veth-peer)', output)
6195 self.assertIn('DHCPREQUEST(veth-peer)', output)
6196 self.assertIn('DHCPREPLY(veth-peer)', output)
6197 self.assertNotIn('rapid-commit', output)
6198
3aa47694 6199 # Check json format
10d670a3 6200 check_json(networkctl_json('veth99'))
3aa47694 6201
e1ef7771 6202 def test_dhcp_client_ipv6_dbus_status(self):
e1ef7771 6203 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
e1ef7771 6204 start_networkd()
95e1fbba 6205 self.wait_online('veth-peer:carrier')
e1ef7771 6206
6207 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
fcdd21ec 6208 # bit set) has yet been received and the configuration does not include WithoutRA=true
e081ffc1 6209 state = get_dhcp6_client_state('veth99')
46f2eb51 6210 print(f"DHCPv6 client state = {state}")
e1ef7771 6211 self.assertEqual(state, 'stopped')
6212
46f2eb51
YW
6213 state = get_dhcp4_client_state('veth99')
6214 print(f"DHCPv4 client state = {state}")
6215 self.assertEqual(state, 'selecting')
6216
6217 start_dnsmasq('--dhcp-option=108,00:00:02:00')
95e1fbba 6218 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 6219
e081ffc1 6220 state = get_dhcp6_client_state('veth99')
46f2eb51 6221 print(f"DHCPv6 client state = {state}")
e1ef7771 6222 self.assertEqual(state, 'bound')
6223
46f2eb51
YW
6224 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
6225 for _ in range(100):
6226 state = get_dhcp4_client_state('veth99')
6227 if state == 'stopped':
6228 break
6229 time.sleep(.2)
6230
6231 print(f"DHCPv4 client state = {state}")
6232 self.assertEqual(state, 'stopped')
6233
aa7336f1
YW
6234 # restart dnsmasq to clear log
6235 stop_dnsmasq()
6236 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6237
6238 # Test renew command
6239 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
10d670a3 6240 networkctl('renew', 'veth99')
aa7336f1
YW
6241
6242 for _ in range(100):
6243 state = get_dhcp4_client_state('veth99')
6244 if state == 'stopped':
6245 break
6246 time.sleep(.2)
6247
6248 print(f"DHCPv4 client state = {state}")
6249 self.assertEqual(state, 'stopped')
6250
6251 print('## dnsmasq log')
6252 output = read_dnsmasq_log_file()
6253 print(output)
6254 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
6255 self.assertIn('DHCPOFFER(veth-peer)', output)
6256 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6257 self.assertNotIn('DHCPACK(veth-peer)', output)
6258
e448fcd0
SS
6259 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
6260 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
6261
6262 start_networkd()
95e1fbba 6263 self.wait_online('veth-peer:carrier')
e448fcd0 6264 start_dnsmasq()
95e1fbba 6265 self.wait_online('veth99:routable', 'veth-peer:routable')
e448fcd0
SS
6266
6267 # checking address
6268 output = check_output('ip address show dev veth99 scope global')
6269 print(output)
6270 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6271 self.assertNotIn('192.168.5', output)
6272
6273 print('## dnsmasq log')
6274 output = read_dnsmasq_log_file()
6275 print(output)
6276 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
6277 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6278 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6279 self.assertIn('DHCPREPLY(veth-peer)', output)
6280 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6281
1f0e3109 6282 def test_dhcp_client_ipv4_only(self):
a962d857 6283 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
1f0e3109 6284
c742d7e8
TM
6285 self.setup_nftset('addr4', 'ipv4_addr')
6286 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6287 self.setup_nftset('ifindex', 'iface_index')
6288
2cf6fdff 6289 start_networkd()
95e1fbba 6290 self.wait_online('veth-peer:carrier')
a962d857 6291 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
b5c8f471 6292 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
a962d857
YW
6293 '--dhcp-option=option:domain-search,example.com',
6294 '--dhcp-alternate-port=67,5555',
6295 ipv4_range='192.168.5.110,192.168.5.119')
95e1fbba 6296 self.wait_online('veth99:routable', 'veth-peer:routable')
9e7d91ed 6297 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
1f0e3109 6298
18f2638f
YW
6299 print('## ip address show dev veth99 scope global')
6300 output = check_output('ip address show dev veth99 scope global')
1f0e3109 6301 print(output)
18f2638f
YW
6302 self.assertIn('mtu 1492', output)
6303 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6304 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')
6305 self.assertNotIn('2600::', output)
195a18c1 6306
6b524d70
YW
6307 output = check_output('ip -4 --json address show dev veth99')
6308 for i in json.loads(output)[0]['addr_info']:
6309 if i['label'] == 'test-label':
6310 address1 = i['local']
6311 break
6312 else:
6313 self.assertFalse(True)
6314
6315 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
6316
18f2638f
YW
6317 print('## ip route show table main dev veth99')
6318 output = check_output('ip route show table main dev veth99')
195a18c1 6319 print(output)
18f2638f
YW
6320 # no DHCP routes assigned to the main table
6321 self.assertNotIn('proto dhcp', output)
6322 # static routes
6323 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6324 self.assertIn('192.168.5.0/24 proto static scope link', output)
6325 self.assertIn('192.168.6.0/24 proto static scope link', output)
6326 self.assertIn('192.168.7.0/24 proto static scope link', output)
6327
6328 print('## ip route show table 211 dev veth99')
6329 output = check_output('ip route show table 211 dev veth99')
6330 print(output)
6b524d70
YW
6331 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6332 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6333 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6334 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6335 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
18f2638f
YW
6336 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6337
1a8e1d78
YW
6338 print('## link state file')
6339 output = read_link_state_file('veth99')
6340 print(output)
b5c8f471 6341 # checking DNS server, SIP server, and Domains
1a8e1d78 6342 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
b5c8f471 6343 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
1a8e1d78 6344 self.assertIn('DOMAINS=example.com', output)
195a18c1 6345
b5c8f471 6346 print('## json')
10d670a3 6347 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
6348
6349 self.assertEqual(len(j['DNS']), 2)
6350 for i in j['DNS']:
6351 print(i)
6352 self.assertEqual(i['Family'], 2)
6353 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6354 self.assertRegex(a, '^192.168.5.[67]$')
6355 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6356 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6357 self.assertEqual('192.168.5.1', a)
6358
6359 self.assertEqual(len(j['SIP']), 2)
6360 for i in j['SIP']:
6361 print(i)
6362 self.assertEqual(i['Family'], 2)
6363 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6364 self.assertRegex(a, '^192.168.5.2[12]$')
6365 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6366 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6367 self.assertEqual('192.168.5.1', a)
6368
18f2638f 6369 print('## dnsmasq log')
063c7e9b
YW
6370 output = read_dnsmasq_log_file()
6371 print(output)
6372 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 6373 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
063c7e9b
YW
6374 self.assertIn('client provides name: test-hostname', output)
6375 self.assertIn('26:mtu', output)
18f2638f
YW
6376
6377 # change address range, DNS servers, and Domains
888f57c1 6378 stop_dnsmasq()
a962d857 6379 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
b5c8f471 6380 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
a962d857
YW
6381 '--dhcp-option=option:domain-search,foo.example.com',
6382 '--dhcp-alternate-port=67,5555',
6383 ipv4_range='192.168.5.120,192.168.5.129',)
195a18c1
YW
6384
6385 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c 6386 print('Wait for the DHCP lease to be expired')
6b524d70 6387 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
4b31fc88 6388 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
195a18c1 6389
95e1fbba 6390 self.wait_online('veth99:routable', 'veth-peer:routable')
195a18c1 6391
18f2638f
YW
6392 print('## ip address show dev veth99 scope global')
6393 output = check_output('ip address show dev veth99 scope global')
195a18c1 6394 print(output)
18f2638f
YW
6395 self.assertIn('mtu 1492', output)
6396 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6b524d70 6397 self.assertNotIn(f'{address1}', output)
18f2638f
YW
6398 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')
6399 self.assertNotIn('2600::', output)
195a18c1 6400
6b524d70
YW
6401 output = check_output('ip -4 --json address show dev veth99')
6402 for i in json.loads(output)[0]['addr_info']:
6403 if i['label'] == 'test-label':
6404 address2 = i['local']
6405 break
6406 else:
6407 self.assertFalse(True)
6408
6409 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
6410
18f2638f
YW
6411 print('## ip route show table main dev veth99')
6412 output = check_output('ip route show table main dev veth99')
195a18c1 6413 print(output)
18f2638f
YW
6414 # no DHCP routes assigned to the main table
6415 self.assertNotIn('proto dhcp', output)
6416 # static routes
6417 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6418 self.assertIn('192.168.5.0/24 proto static scope link', output)
6419 self.assertIn('192.168.6.0/24 proto static scope link', output)
6420 self.assertIn('192.168.7.0/24 proto static scope link', output)
6421
6422 print('## ip route show table 211 dev veth99')
6423 output = check_output('ip route show table 211 dev veth99')
6424 print(output)
6b524d70
YW
6425 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6426 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6427 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
18f2638f 6428 self.assertNotIn('192.168.5.6', output)
6b524d70
YW
6429 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6430 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
18f2638f
YW
6431 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6432
1a8e1d78
YW
6433 print('## link state file')
6434 output = read_link_state_file('veth99')
6435 print(output)
b5c8f471 6436 # checking DNS server, SIP server, and Domains
1a8e1d78 6437 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
b5c8f471 6438 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
1a8e1d78 6439 self.assertIn('DOMAINS=foo.example.com', output)
18f2638f 6440
b5c8f471 6441 print('## json')
10d670a3 6442 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
6443
6444 self.assertEqual(len(j['DNS']), 3)
6445 for i in j['DNS']:
6446 print(i)
6447 self.assertEqual(i['Family'], 2)
6448 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6449 self.assertRegex(a, '^192.168.5.[178]$')
6450 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6451 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6452 self.assertEqual('192.168.5.1', a)
6453
6454 self.assertEqual(len(j['SIP']), 2)
6455 for i in j['SIP']:
6456 print(i)
6457 self.assertEqual(i['Family'], 2)
6458 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6459 self.assertRegex(a, '^192.168.5.2[34]$')
6460 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6461 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6462 self.assertEqual('192.168.5.1', a)
6463
18f2638f 6464 print('## dnsmasq log')
063c7e9b
YW
6465 output = read_dnsmasq_log_file()
6466 print(output)
6467 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 6468 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
063c7e9b
YW
6469 self.assertIn('client provides name: test-hostname', output)
6470 self.assertIn('26:mtu', output)
1f0e3109 6471
0677130e 6472 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
a4640bed 6473
c742d7e8
TM
6474 self.check_nftset('addr4', r'192\.168\.5\.1')
6475 self.check_nftset('network4', r'192\.168\.5\.0/24')
6476 self.check_nftset('ifindex', 'veth99')
6477
6478 self.teardown_nftset('addr4', 'network4', 'ifindex')
6479
e1ef7771 6480 def test_dhcp_client_ipv4_dbus_status(self):
e1ef7771 6481 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
e1ef7771 6482 start_networkd()
95e1fbba 6483 self.wait_online('veth-peer:carrier')
e1ef7771 6484
e081ffc1 6485 state = get_dhcp4_client_state('veth99')
e1ef7771 6486 print(f"State = {state}")
6b524d70 6487 self.assertEqual(state, 'rebooting')
e1ef7771 6488
6489 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6490 '--dhcp-option=option:domain-search,example.com',
6491 '--dhcp-alternate-port=67,5555',
6492 ipv4_range='192.168.5.110,192.168.5.119')
95e1fbba 6493 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 6494 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6495
e081ffc1 6496 state = get_dhcp4_client_state('veth99')
e1ef7771 6497 print(f"State = {state}")
6498 self.assertEqual(state, 'bound')
6499
b65c5390
YW
6500 def test_dhcp_client_allow_list(self):
6501 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
6502
6503 start_networkd()
95e1fbba 6504 self.wait_online('veth-peer:carrier')
b65c5390
YW
6505 since = datetime.datetime.now()
6506 start_dnsmasq()
6507
45c2bbba 6508 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
b65c5390
YW
6509
6510 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6511 since = datetime.datetime.now()
6512 networkctl_reload()
6513
45c2bbba 6514 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
b65c5390
YW
6515
6516 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6517 since = datetime.datetime.now()
6518 networkctl_reload()
6519
45c2bbba 6520 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
b65c5390 6521
2beecc70
RP
6522 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
6523 def test_dhcp_client_rapid_commit(self):
6524 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6525 start_networkd()
95e1fbba 6526 self.wait_online('veth-peer:carrier')
2beecc70
RP
6527
6528 start_dnsmasq('--dhcp-rapid-commit')
95e1fbba 6529 self.wait_online('veth99:routable', 'veth-peer:routable')
2beecc70
RP
6530 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6531
6532 state = get_dhcp4_client_state('veth99')
6533 print(f"DHCPv4 client state = {state}")
6534 self.assertEqual(state, 'bound')
6535
6536 output = read_dnsmasq_log_file()
6537 self.assertIn('DHCPDISCOVER(veth-peer)', output)
6538 self.assertNotIn('DHCPOFFER(veth-peer)', output)
6539 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6540 self.assertIn('DHCPACK(veth-peer)', output)
6541
46f2eb51
YW
6542 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
6543 copy_network_unit('25-veth.netdev',
6544 '25-dhcp-server-ipv6-only-mode.network',
6545 '25-dhcp-client-ipv6-only-mode.network')
6546 start_networkd()
95e1fbba 6547 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
46f2eb51
YW
6548 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6549
6550 state = get_dhcp4_client_state('veth99')
6551 print(f"State = {state}")
6552 self.assertEqual(state, 'bound')
6553
7c0d36ff 6554 def test_dhcp_client_ipv4_use_routes_gateway(self):
a962d857 6555 first = True
e1220a70 6556 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
a962d857
YW
6557 if first:
6558 first = False
6559 else:
6560 self.tearDown()
6561
6562 print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
e1220a70
YW
6563 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
6564 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
7c0d36ff 6565
e1220a70 6566 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
1e86c833
DDM
6567 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6568 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
6983bb0e
FS
6569 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
6570 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
6571 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
a962d857 6572 copy_network_unit(*testunits, copy_dropins=False)
4c2e1833
YW
6573
6574 start_networkd()
95e1fbba 6575 self.wait_online('veth-peer:carrier')
a962d857
YW
6576 additional_options = [
6577 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6578 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
4001653d 6579 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
a962d857 6580 ]
625772c9 6581 if classless:
a962d857 6582 additional_options += [
86f67600 6583 '--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'
a962d857
YW
6584 ]
6585 start_dnsmasq(*additional_options)
95e1fbba 6586 self.wait_online('veth99:routable', 'veth-peer:routable')
4c2e1833 6587
625772c9 6588 output = check_output('ip -4 route show dev veth99')
4c2e1833 6589 print(output)
4c2e1833 6590
7c0d36ff 6591 # Check UseRoutes=
625772c9
YW
6592 if use_routes:
6593 if classless:
6594 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6595 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
86f67600 6596 self.assertRegex(output, r'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
625772c9
YW
6597 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6598 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6599 else:
4001653d 6600 self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
625772c9 6601 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
4001653d 6602 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6603 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6604 else:
625772c9
YW
6605 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6606 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6607 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6608 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4001653d 6609 self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
625772c9 6610 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
4001653d 6611 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6612 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0d7bd445 6613
7c0d36ff 6614 # Check UseGateway=
625772c9
YW
6615 if use_gateway and (not classless or not use_routes):
6616 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6617 else:
625772c9 6618 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32
YW
6619
6620 # Check route to gateway
6621 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
6622 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6623 else:
625772c9 6624 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6625
e1220a70
YW
6626 # Check RoutesToDNS= and RoutesToNTP=
6627 if dns_and_ntp_routes:
625772c9 6628 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 6629 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0ce86f5e
YW
6630 if use_routes:
6631 if classless:
6632 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6633 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6634 else:
6635 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6636 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32 6637 else:
625772c9 6638 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
e1220a70 6639 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6640 else:
625772c9 6641 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 6642 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6643 self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
e1220a70 6644 self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
0d7bd445 6645
10d670a3 6646 check_json(networkctl_json())
94f0bd62 6647
1f0e3109 6648 def test_dhcp_client_settings_anonymize(self):
a962d857 6649 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
2cf6fdff 6650 start_networkd()
95e1fbba 6651 self.wait_online('veth-peer:carrier')
ec38833c 6652 start_dnsmasq()
95e1fbba 6653 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 6654
063c7e9b
YW
6655 print('## dnsmasq log')
6656 output = read_dnsmasq_log_file()
6657 print(output)
6658 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
6659 self.assertNotIn('test-hostname', output)
6660 self.assertNotIn('26:mtu', output)
1f0e3109 6661
1e498853 6662 def test_dhcp_keep_configuration_dhcp(self):
a962d857
YW
6663 copy_network_unit('25-veth.netdev',
6664 '25-dhcp-server-veth-peer.network',
6665 '25-dhcp-client-keep-configuration-dhcp.network')
2cf6fdff 6666 start_networkd()
95e1fbba 6667 self.wait_online('veth-peer:carrier')
a962d857 6668 start_dnsmasq()
95e1fbba 6669 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 6670
1e498853
YW
6671 output = check_output('ip address show dev veth99 scope global')
6672 print(output)
2b6a24a6
YW
6673 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6674 'valid_lft forever preferred_lft forever')
e40a58b5 6675
5238e957 6676 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
888f57c1 6677 stop_dnsmasq()
1f0e3109
SS
6678
6679 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c
YW
6680 print('Wait for the DHCP lease to be expired')
6681 time.sleep(120)
1f0e3109 6682
2b6a24a6 6683 # The lease address should be kept after the lease expired
1e498853
YW
6684 output = check_output('ip address show dev veth99 scope global')
6685 print(output)
2b6a24a6
YW
6686 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6687 'valid_lft forever preferred_lft forever')
1e498853 6688
2b6a24a6 6689 stop_networkd()
1e498853 6690
2b6a24a6 6691 # The lease address should be kept after networkd stopped
1e498853
YW
6692 output = check_output('ip address show dev veth99 scope global')
6693 print(output)
2b6a24a6
YW
6694 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6695 'valid_lft forever preferred_lft forever')
1e498853 6696
a962d857 6697 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
2347b6b9 6698 f.write('[Network]\nDHCP=no\n')
1e498853 6699
2347b6b9 6700 start_networkd()
95e1fbba 6701 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853 6702
2b6a24a6 6703 # Still the lease address should be kept after networkd restarted
1e498853
YW
6704 output = check_output('ip address show dev veth99 scope global')
6705 print(output)
2b6a24a6
YW
6706 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6707 'valid_lft forever preferred_lft forever')
1e498853
YW
6708
6709 def test_dhcp_keep_configuration_dhcp_on_stop(self):
a962d857
YW
6710 copy_network_unit('25-veth.netdev',
6711 '25-dhcp-server-veth-peer.network',
6712 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
2cf6fdff 6713 start_networkd()
95e1fbba 6714 self.wait_online('veth-peer:carrier')
a962d857 6715 start_dnsmasq()
95e1fbba 6716 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853
YW
6717
6718 output = check_output('ip address show dev veth99 scope global')
6719 print(output)
2b6a24a6 6720 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 6721
888f57c1 6722 stop_dnsmasq()
2b6a24a6 6723 stop_networkd()
1e498853
YW
6724
6725 output = check_output('ip address show dev veth99 scope global')
6726 print(output)
2b6a24a6 6727 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 6728
a962d857 6729 start_networkd()
95e1fbba 6730 self.wait_online('veth-peer:routable')
1e498853
YW
6731
6732 output = check_output('ip address show dev veth99 scope global')
6733 print(output)
2b6a24a6 6734 self.assertNotIn('192.168.5.', output)
1f0e3109 6735
30d3b54e 6736 def test_dhcp_client_reuse_address_as_static(self):
a962d857 6737 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
2cf6fdff 6738 start_networkd()
95e1fbba 6739 self.wait_online('veth-peer:carrier')
ec38833c 6740 start_dnsmasq()
95e1fbba 6741 self.wait_online('veth99:routable', 'veth-peer:routable')
2629df47
YW
6742
6743 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 6744 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
426654d7 6745 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
30d3b54e 6746
371810d1 6747 output = check_output('ip address show dev veth99 scope global')
15519a81
YW
6748 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
6749 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
6750 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
30d3b54e
YW
6751 print(static_network)
6752
a962d857 6753 remove_network_unit('25-dhcp-client.network')
30d3b54e 6754
a962d857 6755 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
30d3b54e
YW
6756 f.write(static_network)
6757
15519a81 6758 restart_networkd()
95e1fbba 6759 self.wait_online('veth99:routable')
30d3b54e 6760
371810d1 6761 output = check_output('ip -4 address show dev veth99 scope global')
30d3b54e 6762 print(output)
15519a81
YW
6763 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6764 'valid_lft forever preferred_lft forever')
30d3b54e 6765
371810d1 6766 output = check_output('ip -6 address show dev veth99 scope global')
30d3b54e 6767 print(output)
15519a81
YW
6768 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
6769 'valid_lft forever preferred_lft forever')
30d3b54e 6770
18c613dc
YW
6771 @expectedFailureIfModuleIsNotAvailable('vrf')
6772 def test_dhcp_client_vrf(self):
a962d857
YW
6773 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6774 '25-vrf.netdev', '25-vrf.network')
2cf6fdff 6775 start_networkd()
95e1fbba 6776 self.wait_online('veth-peer:carrier')
ec38833c 6777 start_dnsmasq()
95e1fbba 6778 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
2629df47
YW
6779
6780 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 6781 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
426654d7 6782 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
18c613dc
YW
6783
6784 print('## ip -d link show dev vrf99')
371810d1 6785 output = check_output('ip -d link show dev vrf99')
18c613dc
YW
6786 print(output)
6787 self.assertRegex(output, 'vrf table 42')
6788
6789 print('## ip address show vrf vrf99')
371810d1 6790 output = check_output('ip address show vrf vrf99')
d90f4f7d 6791 print(output)
3e726c15 6792 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 6793 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
d90f4f7d 6794 self.assertRegex(output, 'inet6 .* scope link')
18c613dc
YW
6795
6796 print('## ip address show dev veth99')
371810d1 6797 output = check_output('ip address show dev veth99')
18c613dc 6798 print(output)
3e726c15 6799 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 6800 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
18c613dc
YW
6801 self.assertRegex(output, 'inet6 .* scope link')
6802
6803 print('## ip route show vrf vrf99')
371810d1 6804 output = check_output('ip route show vrf vrf99')
18c613dc
YW
6805 print(output)
6806 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
18c613dc 6807 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
18c613dc
YW
6808 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6809
6810 print('## ip route show table main dev veth99')
371810d1 6811 output = check_output('ip route show table main dev veth99')
18c613dc
YW
6812 print(output)
6813 self.assertEqual(output, '')
6814
af3b1498 6815 def test_dhcp_client_gateway_onlink_implicit(self):
a962d857
YW
6816 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6817 '25-dhcp-client-gateway-onlink-implicit.network')
2cf6fdff 6818 start_networkd()
95e1fbba 6819 self.wait_online('veth-peer:carrier')
ec38833c 6820 start_dnsmasq()
95e1fbba 6821 self.wait_online('veth99:routable', 'veth-peer:routable')
af3b1498 6822
10d670a3 6823 output = networkctl_status('veth99')
af3b1498
YW
6824 print(output)
6825 self.assertRegex(output, '192.168.5')
6826
371810d1 6827 output = check_output('ip route list dev veth99 10.0.0.0/8')
af3b1498
YW
6828 print(output)
6829 self.assertRegex(output, 'onlink')
371810d1 6830 output = check_output('ip route list dev veth99 192.168.100.0/24')
af3b1498
YW
6831 print(output)
6832 self.assertRegex(output, 'onlink')
6833
2d7a594f 6834 def test_dhcp_client_with_ipv4ll(self):
a962d857
YW
6835 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6836 '25-dhcp-client-with-ipv4ll.network')
2cf6fdff 6837 start_networkd()
2d7a594f
YW
6838 # we need to increase timeout above default, as this will need to wait for
6839 # systemd-networkd to get the dhcpv4 transient failure event
95e1fbba 6840 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
63c598ed 6841
2d7a594f 6842 output = check_output('ip -4 address show dev veth99')
63c598ed 6843 print(output)
2d7a594f 6844 self.assertNotIn('192.168.5.', output)
72c747e6 6845 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
63c598ed 6846
a962d857 6847 start_dnsmasq()
2d7a594f
YW
6848 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6849 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
6850 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')
95e1fbba 6851 self.wait_online('veth99:routable')
63c598ed 6852
2d7a594f 6853 output = check_output('ip -4 address show dev veth99')
63c598ed 6854 print(output)
3e726c15 6855 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
2d7a594f
YW
6856 self.assertNotIn('169.254.', output)
6857 self.assertNotIn('scope link', output)
63c598ed 6858
2d7a594f
YW
6859 stop_dnsmasq()
6860 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
286bf3a9 6861 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)
72c747e6 6862 self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
117a55c7 6863
2d7a594f 6864 output = check_output('ip -4 address show dev veth99')
117a55c7 6865 print(output)
2d7a594f 6866 self.assertNotIn('192.168.5.', output)
72c747e6 6867 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
240e4137 6868
3d8e0aa2
YW
6869 def test_dhcp_client_use_dns(self):
6870 def check(self, ipv4, ipv6):
a962d857
YW
6871 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6872 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
3d8e0aa2
YW
6873 f.write('[DHCPv4]\nUseDNS=')
6874 f.write('yes' if ipv4 else 'no')
6875 f.write('\n[DHCPv6]\nUseDNS=')
6876 f.write('yes' if ipv6 else 'no')
6877 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
6878
a962d857 6879 networkctl_reload()
95e1fbba 6880 self.wait_online('veth99:routable')
e2d5aab3 6881
3d8e0aa2
YW
6882 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6883 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6884 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
e2d5aab3 6885
3d8e0aa2 6886 # make resolved re-read the link state file
10d670a3 6887 resolvectl('revert', 'veth99')
e2d5aab3 6888
10d670a3 6889 output = resolvectl('dns', 'veth99')
3d8e0aa2
YW
6890 print(output)
6891 if ipv4:
6892 self.assertIn('192.168.5.1', output)
6893 else:
6894 self.assertNotIn('192.168.5.1', output)
6895 if ipv6:
6896 self.assertIn('2600::1', output)
6897 else:
6898 self.assertNotIn('2600::1', output)
e2d5aab3 6899
10d670a3 6900 check_json(networkctl_json())
e2d5aab3 6901
a962d857 6902 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
e2d5aab3
YW
6903
6904 start_networkd()
95e1fbba 6905 self.wait_online('veth-peer:carrier')
a962d857
YW
6906 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6907 '--dhcp-option=option6:dns-server,[2600::1]')
e2d5aab3 6908
3d8e0aa2
YW
6909 check(self, True, True)
6910 check(self, True, False)
6911 check(self, False, True)
6912 check(self, False, False)
864c7980
YW
6913
6914 def test_dhcp_client_default_use_domains(self):
d51377ac 6915 def check(self, common, ipv4, ipv6):
fb573007
HL
6916 mkdir_p(networkd_conf_dropin_dir)
6917 with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
d51377ac
YW
6918 f.write('[Network]\nUseDomains=')
6919 f.write('yes\n' if common else 'no\n')
fb573007
HL
6920 f.write('[DHCPv4]\nUseDomains=')
6921 f.write('yes\n' if ipv4 else 'no\n')
6922 f.write('[DHCPv6]\nUseDomains=')
6923 f.write('yes\n' if ipv6 else 'no\n')
864c7980 6924
fb573007
HL
6925 restart_networkd()
6926 self.wait_online('veth-peer:carrier')
6927 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6928 '--dhcp-option=option6:dns-server,[2600::1]',
6929 '--dhcp-option=option:domain-search,example.com',
6930 '--dhcp-option=option6:domain-search,example.com')
6931
6932 self.wait_online('veth99:routable')
6933
6934 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6935 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6936 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6937
6938 for _ in range(20):
6939 output = resolvectl('domain', 'veth99')
d51377ac 6940 if common or ipv4 or ipv6:
fb573007
HL
6941 if 'example.com' in output:
6942 break
6943 else:
6944 if 'example.com' not in output:
6945 break
6946 time.sleep(0.5)
6947 else:
6948 print(output)
d51377ac 6949 print(read_link_state_file('veth99'))
fb573007 6950 self.fail('unexpected domain setting in resolved...')
864c7980 6951
fb573007
HL
6952 stop_dnsmasq()
6953 remove_networkd_conf_dropin('default_use_domains.conf')
6954
6955 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
d51377ac
YW
6956 check(self, True, False, False)
6957 check(self, False, True, True)
6958 check(self, False, True, False)
6959 check(self, False, False, True)
6960 check(self, False, False, False)
e2d5aab3 6961
dbe960f0
RP
6962 def test_dhcp_client_use_captive_portal(self):
6963 def check(self, ipv4, ipv6):
6964 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6965 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6966 f.write('[DHCPv4]\nUseCaptivePortal=')
6967 f.write('yes' if ipv4 else 'no')
6968 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6969 f.write('yes' if ipv6 else 'no')
6970 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6971
6972 networkctl_reload()
95e1fbba 6973 self.wait_online('veth99:routable')
dbe960f0
RP
6974
6975 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6976 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6977 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6978
10d670a3 6979 output = networkctl_status('veth99')
dbe960f0
RP
6980 print(output)
6981 if ipv4 or ipv6:
6982 self.assertIn('Captive Portal: http://systemd.io', output)
6983 else:
6984 self.assertNotIn('Captive Portal: http://systemd.io', output)
6985
10d670a3 6986 check_json(networkctl_json())
dbe960f0
RP
6987
6988 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6989
6990 start_networkd()
95e1fbba 6991 self.wait_online('veth-peer:carrier')
dbe960f0
RP
6992 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6993 '--dhcp-option=option6:103,http://systemd.io')
6994
6995 check(self, True, True)
6996 check(self, True, False)
6997 check(self, False, True)
6998 check(self, False, False)
6999
1219391c
RP
7000 def test_dhcp_client_reject_captive_portal(self):
7001 def check(self, ipv4, ipv6):
7002 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
7003 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
7004 f.write('[DHCPv4]\nUseCaptivePortal=')
7005 f.write('yes' if ipv4 else 'no')
7006 f.write('\n[DHCPv6]\nUseCaptivePortal=')
7007 f.write('yes' if ipv6 else 'no')
7008 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
7009
7010 networkctl_reload()
95e1fbba 7011 self.wait_online('veth99:routable')
1219391c
RP
7012
7013 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
7014 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
7015 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
7016
10d670a3 7017 output = networkctl_status('veth99')
1219391c
RP
7018 print(output)
7019 self.assertNotIn('Captive Portal: ', output)
7020 self.assertNotIn('invalid/url', output)
7021
10d670a3 7022 check_json(networkctl_json())
1219391c
RP
7023
7024 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
7025
7026 start_networkd()
95e1fbba 7027 self.wait_online('veth-peer:carrier')
1219391c
RP
7028 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
7029 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
7030 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
7031
7032 check(self, True, True)
7033 check(self, True, False)
7034 check(self, False, True)
7035 check(self, False, False)
7036
a27588d4 7037class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
caad88a2
YW
7038
7039 def setUp(self):
a962d857 7040 setup_common()
caad88a2
YW
7041
7042 def tearDown(self):
a962d857 7043 tear_down_common()
caad88a2 7044
ab06b74f
YW
7045 def check_dhcp6_prefix(self, link):
7046 description = get_link_description(link)
8fb6320e 7047
ab06b74f
YW
7048 self.assertIn('DHCPv6Client', description.keys())
7049 self.assertIn('Prefixes', description['DHCPv6Client'])
8fb6320e 7050
ab06b74f 7051 prefixInfo = description['DHCPv6Client']['Prefixes']
8fb6320e 7052
ab06b74f 7053 self.assertEqual(len(prefixInfo), 1)
8fb6320e 7054
ab06b74f
YW
7055 self.assertIn('Prefix', prefixInfo[0].keys())
7056 self.assertIn('PrefixLength', prefixInfo[0].keys())
7057 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
7058 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
caad88a2 7059
ab06b74f
YW
7060 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
7061 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
7062 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
7063 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
7064
7065 def test_dhcp6pd_no_address(self):
7066 # For issue #29979.
7067 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
c742d7e8 7068
caad88a2 7069 start_networkd()
95e1fbba 7070 self.wait_online('veth-peer:routable')
a962d857 7071 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba 7072 self.wait_online('veth99:degraded')
1805e2cb 7073
1805e2cb
YW
7074 print('### ip -6 address show dev veth99 scope global')
7075 output = check_output('ip -6 address show dev veth99 scope global')
7076 print(output)
7077 self.assertNotIn('inet6 3ffe:501:ffff', output)
caad88a2 7078
ab06b74f 7079 self.check_dhcp6_prefix('veth99')
8fb6320e 7080
3b677c6f
YW
7081 def test_dhcp6pd_no_assign(self):
7082 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
7083 # However, the server does not provide IA_NA. For issue #31349.
7084 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
7085
7086 start_networkd()
7087 self.wait_online('veth-peer:routable')
7088 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
7089 self.wait_online('veth99:degraded')
7090
7091 print('### ip -6 address show dev veth99 scope global')
7092 output = check_output('ip -6 address show dev veth99 scope global')
7093 print(output)
7094 self.assertNotIn('inet6 3ffe:501:ffff', output)
7095
7096 self.check_dhcp6_prefix('veth99')
7097
ab06b74f
YW
7098 def test_dhcp6pd(self):
7099 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
7100 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7101 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7102 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7103 '25-dhcp-pd-downstream-dummy97.network',
7104 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7105 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
8fb6320e 7106
ab06b74f
YW
7107 start_networkd()
7108 self.wait_online('veth-peer:routable')
7109 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba
YW
7110 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7111 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
1805e2cb 7112
ab06b74f
YW
7113 self.setup_nftset('addr6', 'ipv6_addr')
7114 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
7115 self.setup_nftset('ifindex', 'iface_index')
7116
7117 # Check DBus assigned prefix information to veth99
7118 self.check_dhcp6_prefix('veth99')
7119
caad88a2
YW
7120 print('### ip -6 address show dev veth-peer scope global')
7121 output = check_output('ip -6 address show dev veth-peer scope global')
7122 print(output)
7123 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
7124
f7805a6c
FS
7125 # Link Subnet IDs
7126 # test1: 0x00
7127 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
7128 # dummy98: 0x00
7129 # dummy99: auto -> 0x02 (No address assignment)
f7805a6c
FS
7130 # veth97: 0x08
7131 # veth98: 0x09
38488bab 7132 # veth99: 0x10
6016f1cf 7133
caad88a2
YW
7134 print('### ip -6 address show dev veth99 scope global')
7135 output = check_output('ip -6 address show dev veth99 scope global')
7136 print(output)
7137 # IA_NA
7138 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
7139 # address in IA_PD (Token=static)
38488bab 7140 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
caad88a2 7141 # address in IA_PD (Token=eui64)
38488bab
YW
7142 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
7143 # address in IA_PD (temporary)
7144 # Note that the temporary addresses may appear after the link enters configured state
7145 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')
caad88a2
YW
7146
7147 print('### ip -6 address show dev test1 scope global')
7148 output = check_output('ip -6 address show dev test1 scope global')
7149 print(output)
7150 # address in IA_PD (Token=static)
7151 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7152 # address in IA_PD (temporary)
7153 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')
7154
7155 print('### ip -6 address show dev dummy98 scope global')
7156 output = check_output('ip -6 address show dev dummy98 scope global')
7157 print(output)
7158 # address in IA_PD (Token=static)
07b7337a 7159 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7160 # address in IA_PD (temporary)
07b7337a 7161 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')
6016f1cf
YW
7162
7163 print('### ip -6 address show dev dummy99 scope global')
7164 output = check_output('ip -6 address show dev dummy99 scope global')
7165 print(output)
7166 # Assign=no
07b7337a 7167 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2 7168
6c8d6bdd
YW
7169 print('### ip -6 address show dev veth97 scope global')
7170 output = check_output('ip -6 address show dev veth97 scope global')
7171 print(output)
7172 # address in IA_PD (Token=static)
7173 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7174 # address in IA_PD (Token=eui64)
7175 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7176 # address in IA_PD (temporary)
7177 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')
7178
7179 print('### ip -6 address show dev veth97-peer scope global')
7180 output = check_output('ip -6 address show dev veth97-peer scope global')
7181 print(output)
7182 # NDisc address (Token=static)
7183 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7184 # NDisc address (Token=eui64)
7185 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7186 # NDisc address (temporary)
7187 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')
7188
caad88a2
YW
7189 print('### ip -6 address show dev veth98 scope global')
7190 output = check_output('ip -6 address show dev veth98 scope global')
7191 print(output)
7192 # address in IA_PD (Token=static)
7193 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7194 # address in IA_PD (Token=eui64)
7195 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7196 # address in IA_PD (temporary)
7197 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')
7198
7199 print('### ip -6 address show dev veth98-peer scope global')
7200 output = check_output('ip -6 address show dev veth98-peer scope global')
7201 print(output)
7202 # NDisc address (Token=static)
7203 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7204 # NDisc address (Token=eui64)
7205 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7206 # NDisc address (temporary)
7207 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')
7208
caad88a2
YW
7209 print('### ip -6 route show type unreachable')
7210 output = check_output('ip -6 route show type unreachable')
7211 print(output)
7212 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
7213
7214 print('### ip -6 route show dev veth99')
7215 output = check_output('ip -6 route show dev veth99')
7216 print(output)
38488bab 7217 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7218
7219 print('### ip -6 route show dev test1')
7220 output = check_output('ip -6 route show dev test1')
7221 print(output)
7222 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7223
7224 print('### ip -6 route show dev dummy98')
7225 output = check_output('ip -6 route show dev dummy98')
7226 print(output)
07b7337a 7227 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7228
7229 print('### ip -6 route show dev dummy99')
7230 output = check_output('ip -6 route show dev dummy99')
7231 print(output)
07b7337a 7232 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6016f1cf
YW
7233
7234 print('### ip -6 route show dev veth97')
7235 output = check_output('ip -6 route show dev veth97')
7236 print(output)
7237 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
7238
7239 print('### ip -6 route show dev veth97-peer')
7240 output = check_output('ip -6 route show dev veth97-peer')
7241 print(output)
7242 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
caad88a2
YW
7243
7244 print('### ip -6 route show dev veth98')
7245 output = check_output('ip -6 route show dev veth98')
7246 print(output)
7247 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
7248
7249 print('### ip -6 route show dev veth98-peer')
7250 output = check_output('ip -6 route show dev veth98-peer')
7251 print(output)
7252 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
7253
7254 # Test case for a downstream which appears later
7255 check_output('ip link add dummy97 type dummy')
95e1fbba 7256 self.wait_online('dummy97:routable')
caad88a2
YW
7257
7258 print('### ip -6 address show dev dummy97 scope global')
7259 output = check_output('ip -6 address show dev dummy97 scope global')
7260 print(output)
7261 # address in IA_PD (Token=static)
6016f1cf 7262 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7263 # address in IA_PD (temporary)
6016f1cf 7264 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')
caad88a2
YW
7265
7266 print('### ip -6 route show dev dummy97')
7267 output = check_output('ip -6 route show dev dummy97')
7268 print(output)
6016f1cf 7269 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7270
7271 # Test case for reconfigure
a962d857 7272 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 7273 self.wait_online('dummy98:routable', 'dummy99:degraded')
caad88a2
YW
7274
7275 print('### ip -6 address show dev dummy98 scope global')
7276 output = check_output('ip -6 address show dev dummy98 scope global')
7277 print(output)
7278 # address in IA_PD (Token=static)
07b7337a 7279 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7280 # address in IA_PD (temporary)
07b7337a 7281 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')
6016f1cf
YW
7282
7283 print('### ip -6 address show dev dummy99 scope global')
7284 output = check_output('ip -6 address show dev dummy99 scope global')
7285 print(output)
7286 # Assign=no
07b7337a 7287 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2
YW
7288
7289 print('### ip -6 route show dev dummy98')
7290 output = check_output('ip -6 route show dev dummy98')
7291 print(output)
07b7337a 7292 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6016f1cf
YW
7293
7294 print('### ip -6 route show dev dummy99')
7295 output = check_output('ip -6 route show dev dummy99')
7296 print(output)
07b7337a 7297 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
caad88a2 7298
a4640bed
TM
7299 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
7300
c742d7e8
TM
7301 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
7302 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
7303 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
7304 self.check_nftset('ifindex', 'dummy98')
7305
7306 self.teardown_nftset('addr6', 'network6', 'ifindex')
7307
6a936c9c 7308 def verify_dhcp4_6rd(self, tunnel_name):
84cc85f9
YW
7309 print('### ip -4 address show dev veth-peer scope global')
7310 output = check_output('ip -4 address show dev veth-peer scope global')
7311 print(output)
7312 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
7313
f7805a6c
FS
7314 # Link Subnet IDs
7315 # test1: 0x00
7316 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
7317 # dummy98: 0x00
7318 # dummy99: auto -> 0x0[23] (No address assignment)
7319 # 6rd-XXX: auto -> 0x0[23]
f7805a6c
FS
7320 # veth97: 0x08
7321 # veth98: 0x09
7322 # veth99: 0x10
84cc85f9
YW
7323
7324 print('### ip -4 address show dev veth99 scope global')
7325 output = check_output('ip -4 address show dev veth99 scope global')
7326 print(output)
7327 self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7328
7329 print('### ip -6 address show dev veth99 scope global')
7330 output = check_output('ip -6 address show dev veth99 scope global')
7331 print(output)
7332 # address in IA_PD (Token=static)
7333 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7334 # address in IA_PD (Token=eui64)
7335 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7336 # address in IA_PD (temporary)
7337 # Note that the temporary addresses may appear after the link enters configured state
7338 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')
7339
7340 print('### ip -6 address show dev test1 scope global')
7341 output = check_output('ip -6 address show dev test1 scope global')
7342 print(output)
7343 # address in IA_PD (Token=static)
7344 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7345 # address in IA_PD (temporary)
7346 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')
7347
7348 print('### ip -6 address show dev dummy98 scope global')
7349 output = check_output('ip -6 address show dev dummy98 scope global')
7350 print(output)
7351 # address in IA_PD (Token=static)
07b7337a 7352 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
84cc85f9 7353 # address in IA_PD (temporary)
07b7337a 7354 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')
84cc85f9
YW
7355
7356 print('### ip -6 address show dev dummy99 scope global')
7357 output = check_output('ip -6 address show dev dummy99 scope global')
7358 print(output)
7359 # Assign=no
07b7337a 7360 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
84cc85f9
YW
7361
7362 print('### ip -6 address show dev veth97 scope global')
7363 output = check_output('ip -6 address show dev veth97 scope global')
7364 print(output)
7365 # address in IA_PD (Token=static)
7366 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7367 # address in IA_PD (Token=eui64)
7368 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7369 # address in IA_PD (temporary)
7370 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')
7371
7372 print('### ip -6 address show dev veth97-peer scope global')
7373 output = check_output('ip -6 address show dev veth97-peer scope global')
7374 print(output)
7375 # NDisc address (Token=static)
7376 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7377 # NDisc address (Token=eui64)
7378 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7379 # NDisc address (temporary)
7380 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')
7381
7382 print('### ip -6 address show dev veth98 scope global')
7383 output = check_output('ip -6 address show dev veth98 scope global')
7384 print(output)
7385 # address in IA_PD (Token=static)
7386 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7387 # address in IA_PD (Token=eui64)
7388 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7389 # address in IA_PD (temporary)
7390 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')
7391
7392 print('### ip -6 address show dev veth98-peer scope global')
7393 output = check_output('ip -6 address show dev veth98-peer scope global')
7394 print(output)
7395 # NDisc address (Token=static)
7396 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7397 # NDisc address (Token=eui64)
7398 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7399 # NDisc address (temporary)
7400 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')
7401
7402 print('### ip -6 route show type unreachable')
7403 output = check_output('ip -6 route show type unreachable')
7404 print(output)
7405 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7406
7407 print('### ip -6 route show dev veth99')
7408 output = check_output('ip -6 route show dev veth99')
7409 print(output)
7410 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7411
7412 print('### ip -6 route show dev test1')
7413 output = check_output('ip -6 route show dev test1')
7414 print(output)
7415 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7416
7417 print('### ip -6 route show dev dummy98')
7418 output = check_output('ip -6 route show dev dummy98')
7419 print(output)
07b7337a 7420 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
7421
7422 print('### ip -6 route show dev dummy99')
7423 output = check_output('ip -6 route show dev dummy99')
7424 print(output)
07b7337a 7425 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
84cc85f9
YW
7426
7427 print('### ip -6 route show dev veth97')
7428 output = check_output('ip -6 route show dev veth97')
7429 print(output)
7430 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7431
7432 print('### ip -6 route show dev veth97-peer')
7433 output = check_output('ip -6 route show dev veth97-peer')
7434 print(output)
7435 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7436
7437 print('### ip -6 route show dev veth98')
7438 output = check_output('ip -6 route show dev veth98')
7439 print(output)
7440 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7441
7442 print('### ip -6 route show dev veth98-peer')
7443 output = check_output('ip -6 route show dev veth98-peer')
7444 print(output)
7445 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7446
84cc85f9
YW
7447 print('### ip -6 address show dev dummy97 scope global')
7448 output = check_output('ip -6 address show dev dummy97 scope global')
7449 print(output)
7450 # address in IA_PD (Token=static)
7451 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7452 # address in IA_PD (temporary)
7453 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')
7454
7455 print('### ip -6 route show dev dummy97')
7456 output = check_output('ip -6 route show dev dummy97')
7457 print(output)
7458 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7459
e4295d4d
FS
7460 print(f'### ip -d link show dev {tunnel_name}')
7461 output = check_output(f'ip -d link show dev {tunnel_name}')
84cc85f9
YW
7462 print(output)
7463 self.assertIn('link/sit 10.100.100.', output)
7464 self.assertIn('local 10.100.100.', output)
84cc85f9
YW
7465 self.assertIn('ttl 64', output)
7466 self.assertIn('6rd-prefix 2001:db8::/32', output)
7467 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
7468
e4295d4d
FS
7469 print(f'### ip -6 address show dev {tunnel_name}')
7470 output = check_output(f'ip -6 address show dev {tunnel_name}')
84cc85f9 7471 print(output)
07b7337a 7472 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')
84cc85f9
YW
7473 self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7474
e4295d4d
FS
7475 print(f'### ip -6 route show dev {tunnel_name}')
7476 output = check_output(f'ip -6 route show dev {tunnel_name}')
84cc85f9 7477 print(output)
07b7337a 7478 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
7479 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
7480
7481 print('### ip -6 route show default')
7482 output = check_output('ip -6 route show default')
7483 print(output)
7484 self.assertIn('default', output)
e4295d4d 7485 self.assertIn(f'via ::10.0.0.1 dev {tunnel_name}', output)
84cc85f9 7486
6a936c9c 7487 def test_dhcp4_6rd(self):
e081ffc1
YW
7488 def get_dhcp_6rd_prefix(link):
7489 description = get_link_description(link)
8fb6320e 7490
7491 self.assertIn('DHCPv4Client', description.keys())
7492 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
7493
7494 prefixInfo = description['DHCPv4Client']['6rdPrefix']
7495 self.assertIn('Prefix', prefixInfo.keys())
7496 self.assertIn('PrefixLength', prefixInfo.keys())
7497 self.assertIn('IPv4MaskLength', prefixInfo.keys())
7498 self.assertIn('BorderRouters', prefixInfo.keys())
7499
7500 return prefixInfo
7501
a962d857
YW
7502 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7503 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7504 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7505 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7506 '25-dhcp-pd-downstream-dummy97.network',
7507 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7508 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7509 '80-6rd-tunnel.network')
6a936c9c
YW
7510
7511 start_networkd()
95e1fbba 7512 self.wait_online('veth-peer:routable')
f7805a6c
FS
7513
7514 # ipv4masklen: 8
7515 # 6rd-prefix: 2001:db8::/32
7516 # br-addresss: 10.0.0.1
7517
a962d857
YW
7518 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',
7519 ipv4_range='10.100.100.100,10.100.100.200',
7520 ipv4_router='10.0.0.1')
95e1fbba
YW
7521 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7522 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c 7523
8fb6320e 7524 # Check the DBus interface for assigned prefix information
e081ffc1 7525 prefixInfo = get_dhcp_6rd_prefix('veth99')
8fb6320e 7526
7527 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7528 self.assertEqual(prefixInfo['PrefixLength'], 32)
7529 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
7530 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
7531
6a936c9c
YW
7532 # Test case for a downstream which appears later
7533 check_output('ip link add dummy97 type dummy')
95e1fbba 7534 self.wait_online('dummy97:routable')
6a936c9c
YW
7535
7536 # Find tunnel name
7537 tunnel_name = None
7538 for name in os.listdir('/sys/class/net/'):
7539 if name.startswith('6rd-'):
7540 tunnel_name = name
7541 break
7542
95e1fbba 7543 self.wait_online(f'{tunnel_name}:routable')
6a936c9c
YW
7544
7545 self.verify_dhcp4_6rd(tunnel_name)
7546
7547 # Test case for reconfigure
a962d857 7548 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 7549 self.wait_online('dummy98:routable', 'dummy99:degraded')
6a936c9c
YW
7550
7551 self.verify_dhcp4_6rd(tunnel_name)
7552
3af934bc 7553 print('Wait for the DHCP lease to be renewed/rebind')
a102a52c 7554 time.sleep(120)
6a936c9c 7555
95e1fbba
YW
7556 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7557 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c
YW
7558
7559 self.verify_dhcp4_6rd(tunnel_name)
7560
9633f977 7561class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
9633f977
SS
7562
7563 def setUp(self):
a962d857 7564 setup_common()
9633f977
SS
7565
7566 def tearDown(self):
a962d857 7567 tear_down_common()
9633f977
SS
7568
7569 def test_ipv6_route_prefix(self):
a962d857
YW
7570 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7571 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9633f977
SS
7572
7573 start_networkd()
95e1fbba 7574 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9633f977 7575
635f2a66
YW
7576 output = check_output('ip address show dev veth-peer')
7577 print(output)
7578 self.assertIn('inet6 2001:db8:0:1:', output)
7579 self.assertNotIn('inet6 2001:db8:0:2:', output)
ab47f960 7580 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 7581
3c874fd7 7582 output = check_output('ip -6 route show dev veth-peer')
9633f977 7583 print(output)
635f2a66
YW
7584 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7585 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
ab47f960 7586 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
635f2a66
YW
7587 self.assertIn('2001:db0:fff::/64 via ', output)
7588 self.assertNotIn('2001:db1:fff::/64 via ', output)
ab47f960 7589 self.assertNotIn('2001:db2:fff::/64 via ', output)
9633f977 7590
635f2a66
YW
7591 output = check_output('ip address show dev veth99')
7592 print(output)
7593 self.assertNotIn('inet6 2001:db8:0:1:', output)
fe2a8b3d
YW
7594 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
7595 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
ab47f960 7596 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 7597
10d670a3 7598 output = resolvectl('dns', 'veth-peer')
4a906586
YW
7599 print(output)
7600 self.assertRegex(output, '2001:db8:1:1::2')
7601
10d670a3 7602 output = resolvectl('domain', 'veth-peer')
4a906586
YW
7603 print(output)
7604 self.assertIn('example.com', output)
7605
10d670a3 7606 check_json(networkctl_json())
94f0bd62 7607
10d670a3 7608 output = networkctl_json('veth-peer')
681007ac
SS
7609 check_json(output)
7610
7611 # PREF64 or NAT64
7612 pref64 = json.loads(output)['NDisc']['PREF64'][0]
7613
7614 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
7615 self.assertEqual(prefix, '64:ff9b::')
7616
7617 prefix_length = pref64['PrefixLength']
7618 self.assertEqual(prefix_length, 96)
7619
635f2a66 7620 def test_ipv6_route_prefix_deny_list(self):
a962d857
YW
7621 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7622 '12-dummy.netdev', '25-ipv6ra-uplink.network')
635f2a66
YW
7623
7624 start_networkd()
95e1fbba 7625 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
635f2a66
YW
7626
7627 output = check_output('ip address show dev veth-peer')
7628 print(output)
7629 self.assertIn('inet6 2001:db8:0:1:', output)
7630 self.assertNotIn('inet6 2001:db8:0:2:', output)
7631
7632 output = check_output('ip -6 route show dev veth-peer')
7633 print(output)
7634 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7635 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7636 self.assertIn('2001:db0:fff::/64 via ', output)
7637 self.assertNotIn('2001:db1:fff::/64 via ', output)
7638
7639 output = check_output('ip address show dev veth99')
3c874fd7 7640 print(output)
635f2a66
YW
7641 self.assertNotIn('inet6 2001:db8:0:1:', output)
7642 self.assertIn('inet6 2001:db8:0:2:', output)
3c874fd7 7643
10d670a3 7644 output = resolvectl('dns', 'veth-peer')
4a906586
YW
7645 print(output)
7646 self.assertRegex(output, '2001:db8:1:1::2')
7647
10d670a3 7648 output = resolvectl('domain', 'veth-peer')
4a906586
YW
7649 print(output)
7650 self.assertIn('example.com', output)
7651
7db05447 7652class NetworkdMTUTests(unittest.TestCase, Utilities):
7db05447
DS
7653
7654 def setUp(self):
a962d857 7655 setup_common()
7db05447
DS
7656
7657 def tearDown(self):
a962d857 7658 tear_down_common()
7db05447
DS
7659
7660 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
7661 if not ipv6_mtu:
7662 ipv6_mtu = mtu
7663
7664 # test normal start
7665 start_networkd()
95e1fbba 7666 self.wait_online('dummy98:routable')
a962d857
YW
7667 self.check_link_attr('dummy98', 'mtu', mtu)
7668 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
7669
7670 # test normal restart
7671 restart_networkd()
95e1fbba 7672 self.wait_online('dummy98:routable')
a962d857
YW
7673 self.check_link_attr('dummy98', 'mtu', mtu)
7674 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
7675
7676 if reset:
7677 self.reset_check_mtu(mtu, ipv6_mtu)
7678
7679 def reset_check_mtu(self, mtu, ipv6_mtu=None):
7680 ''' test setting mtu/ipv6_mtu with interface already up '''
7681 stop_networkd()
7682
7683 # note - changing the device mtu resets the ipv6 mtu
a962d857
YW
7684 check_output('ip link set up mtu 1501 dev dummy98')
7685 check_output('ip link set up mtu 1500 dev dummy98')
7686 self.check_link_attr('dummy98', 'mtu', '1500')
7687 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7db05447
DS
7688
7689 self.check_mtu(mtu, ipv6_mtu, reset=False)
7690
7691 def test_mtu_network(self):
a962d857 7692 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7db05447
DS
7693 self.check_mtu('1600')
7694
7695 def test_mtu_netdev(self):
a962d857 7696 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
7db05447
DS
7697 # note - MTU set by .netdev happens ONLY at device creation!
7698 self.check_mtu('1600', reset=False)
7699
7700 def test_mtu_link(self):
a962d857 7701 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
7db05447
DS
7702 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7703 self.check_mtu('1600', reset=False)
7704
7705 def test_ipv6_mtu(self):
7706 ''' set ipv6 mtu without setting device mtu '''
a962d857 7707 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7db05447
DS
7708 self.check_mtu('1500', '1400')
7709
7710 def test_ipv6_mtu_toolarge(self):
7711 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
a962d857 7712 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7713 self.check_mtu('1500', '1500')
7714
7715 def test_mtu_network_ipv6_mtu(self):
7716 ''' set ipv6 mtu and set device mtu via network file '''
a962d857 7717 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7718 self.check_mtu('1600', '1550')
7719
7720 def test_mtu_netdev_ipv6_mtu(self):
7721 ''' set ipv6 mtu and set device mtu via netdev file '''
a962d857 7722 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7723 self.check_mtu('1600', '1550', reset=False)
7724
7725 def test_mtu_link_ipv6_mtu(self):
7726 ''' set ipv6 mtu and set device mtu via link file '''
a962d857 7727 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7728 self.check_mtu('1600', '1550', reset=False)
7729
7730
1f0e3109 7731if __name__ == '__main__':
9c1ae484
YW
7732 parser = argparse.ArgumentParser()
7733 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
33b0e0c0 7734 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
9c1ae484
YW
7735 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
7736 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
94c03122 7737 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
fa4c6095 7738 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
94c03122 7739 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
6c9efba6 7740 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)
a561bcee 7741 ns, unknown_args = parser.parse_known_args(namespace=unittest)
9c1ae484
YW
7742
7743 if ns.build_dir:
9c1ae484 7744 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
b6d587d1 7745 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
b05c4d6b 7746 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
9c1ae484
YW
7747 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
7748 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
b6d587d1
YW
7749 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
7750 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
b05c4d6b 7751 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
f66045c7 7752 build_dir = ns.build_dir
9c1ae484 7753
33b0e0c0 7754 if ns.source_dir:
f66045c7 7755 source_dir = ns.source_dir
33b0e0c0 7756 else:
f66045c7
YW
7757 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
7758 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
33b0e0c0 7759
9c1ae484
YW
7760 use_valgrind = ns.use_valgrind
7761 enable_debug = ns.enable_debug
94c03122 7762 asan_options = ns.asan_options
fa4c6095 7763 lsan_options = ns.lsan_options
94c03122 7764 ubsan_options = ns.ubsan_options
6c9efba6 7765 with_coverage = ns.with_coverage
9c1ae484
YW
7766
7767 if use_valgrind:
b05c4d6b
YW
7768 # Do not forget the trailing space.
7769 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7770
7771 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
7772 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
7773 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
7774 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
7775 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
9c1ae484 7776
9dcdf16b
YW
7777 if build_dir:
7778 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
7779 else:
7780 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
7781
94c03122 7782 if asan_options:
a962d857 7783 env.update({'ASAN_OPTIONS': asan_options})
fa4c6095 7784 if lsan_options:
a962d857 7785 env.update({'LSAN_OPTIONS': lsan_options})
94c03122 7786 if ubsan_options:
a962d857 7787 env.update({'UBSAN_OPTIONS': ubsan_options})
b05c4d6b
YW
7788 if use_valgrind:
7789 env.update({'SYSTEMD_MEMPOOL': '0'})
9c1ae484 7790
163d095f
YW
7791 wait_online_env = env.copy()
7792 if enable_debug:
a962d857 7793 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
163d095f 7794
a561bcee 7795 sys.argv[1:] = unknown_args
0765763e 7796 unittest.main(verbosity=3)