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