]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-network/systemd-networkd-tests.py
Merge pull request #32195 from yuwata/network-ndisc-mtu
[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
336d18f0 4626class NetworkdStateFileTests(unittest.TestCase, Utilities):
336d18f0
YW
4627
4628 def setUp(self):
a962d857 4629 setup_common()
336d18f0
YW
4630
4631 def tearDown(self):
a962d857 4632 tear_down_common()
336d18f0
YW
4633
4634 def test_state_file(self):
a962d857 4635 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
336d18f0 4636 start_networkd()
95e1fbba 4637 self.wait_online('dummy98:routable')
336d18f0 4638
f91b2340 4639 # make link state file updated
10d670a3 4640 resolvectl('revert', 'dummy98')
336d18f0 4641
10d670a3 4642 check_json(networkctl_json())
94f0bd62 4643
1a8e1d78
YW
4644 output = read_link_state_file('dummy98')
4645 print(output)
4646 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
4647 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
4648 self.assertIn('ADMIN_STATE=configured', output)
4649 self.assertIn('OPER_STATE=routable', output)
4650 self.assertIn('REQUIRED_FOR_ONLINE=yes', output)
4651 self.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output)
4652 self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output)
4653 self.assertIn('ACTIVATION_POLICY=up', output)
4654 self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output)
4655 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
4656 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4657 self.assertIn('DOMAINS=hogehoge', output)
4658 self.assertIn('ROUTE_DOMAINS=foofoo', output)
4659 self.assertIn('LLMNR=no', output)
4660 self.assertIn('MDNS=yes', output)
4661 self.assertIn('DNSSEC=no', output)
336d18f0 4662
10d670a3
YW
4663 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4664 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4665 resolvectl('llmnr', 'dummy98', 'yes')
4666 resolvectl('mdns', 'dummy98', 'no')
4667 resolvectl('dnssec', 'dummy98', 'yes')
4668 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
336d18f0 4669
10d670a3 4670 check_json(networkctl_json())
94f0bd62 4671
1a8e1d78
YW
4672 output = read_link_state_file('dummy98')
4673 print(output)
4674 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
4675 self.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output)
4676 self.assertIn('DOMAINS=hogehogehoge', output)
4677 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
4678 self.assertIn('LLMNR=yes', output)
4679 self.assertIn('MDNS=no', output)
4680 self.assertIn('DNSSEC=yes', output)
336d18f0 4681
10d670a3 4682 timedatectl('revert', 'dummy98')
336d18f0 4683
10d670a3 4684 check_json(networkctl_json())
94f0bd62 4685
1a8e1d78
YW
4686 output = read_link_state_file('dummy98')
4687 print(output)
4688 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
4689 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4690 self.assertIn('DOMAINS=hogehogehoge', output)
4691 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
4692 self.assertIn('LLMNR=yes', output)
4693 self.assertIn('MDNS=no', output)
4694 self.assertIn('DNSSEC=yes', output)
336d18f0 4695
10d670a3 4696 resolvectl('revert', 'dummy98')
336d18f0 4697
10d670a3 4698 check_json(networkctl_json())
94f0bd62 4699
1a8e1d78
YW
4700 output = read_link_state_file('dummy98')
4701 print(output)
4702 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
4703 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
4704 self.assertIn('DOMAINS=hogehoge', output)
4705 self.assertIn('ROUTE_DOMAINS=foofoo', output)
4706 self.assertIn('LLMNR=no', output)
4707 self.assertIn('MDNS=yes', output)
4708 self.assertIn('DNSSEC=no', output)
336d18f0 4709
ee3cbfdb
YW
4710 def test_address_state(self):
4711 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4712 start_networkd()
4713
95e1fbba 4714 self.wait_online('dummy98:degraded')
ee3cbfdb
YW
4715
4716 output = read_link_state_file('dummy98')
4717 self.assertIn('IPV4_ADDRESS_STATE=off', output)
4718 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
4719
4720 # with a routable IPv4 address
4721 check_output('ip address add 10.1.2.3/16 dev dummy98')
95e1fbba
YW
4722 self.wait_online('dummy98:routable', ipv4=True)
4723 self.wait_online('dummy98:routable')
ee3cbfdb
YW
4724
4725 output = read_link_state_file('dummy98')
4726 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
4727 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
4728
4729 check_output('ip address del 10.1.2.3/16 dev dummy98')
4730
4731 # with a routable IPv6 address
4732 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
95e1fbba
YW
4733 self.wait_online('dummy98:routable', ipv6=True)
4734 self.wait_online('dummy98:routable')
ee3cbfdb
YW
4735
4736 output = read_link_state_file('dummy98')
4737 self.assertIn('IPV4_ADDRESS_STATE=off', output)
4738 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
4739
be68c2c9 4740class NetworkdBondTests(unittest.TestCase, Utilities):
c3a8853f
YW
4741
4742 def setUp(self):
a962d857 4743 setup_common()
c3a8853f
YW
4744
4745 def tearDown(self):
a962d857 4746 tear_down_common()
c3a8853f 4747
b06469a6
YW
4748 def test_bond_keep_master(self):
4749 check_output('ip link add bond199 type bond mode active-backup')
4750 check_output('ip link add dummy98 type dummy')
4751 check_output('ip link set dummy98 master bond199')
4752
a962d857 4753 copy_network_unit('23-keep-master.network')
b06469a6 4754 start_networkd()
95e1fbba 4755 self.wait_online('dummy98:enslaved')
b06469a6
YW
4756
4757 output = check_output('ip -d link show bond199')
4758 print(output)
4759 self.assertRegex(output, 'active_slave dummy98')
4760
4761 output = check_output('ip -d link show dummy98')
4762 print(output)
4763 self.assertRegex(output, 'master bond199')
4764
c2990ec3 4765 def test_bond_active_slave(self):
a962d857 4766 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 4767 start_networkd()
95e1fbba 4768 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 4769
371810d1 4770 output = check_output('ip -d link show bond199')
c2990ec3 4771 print(output)
b448fc0a 4772 self.assertIn('active_slave dummy98', output)
c2990ec3 4773
2bb1d3c1
YW
4774 # test case for issue #31165.
4775 since = datetime.datetime.now()
4776 networkctl_reconfigure('dummy98')
95e1fbba 4777 self.wait_online('dummy98:enslaved', 'bond199:degraded')
2bb1d3c1
YW
4778 self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
4779
c2990ec3 4780 def test_bond_primary_slave(self):
a962d857 4781 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 4782 start_networkd()
95e1fbba 4783 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 4784
371810d1 4785 output = check_output('ip -d link show bond199')
c2990ec3 4786 print(output)
b448fc0a
YW
4787 self.assertIn('primary dummy98', output)
4788
4789 # for issue #25627
4790 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
4791 for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4792 with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
4793 f.write(f'[Link]\nMACAddress={mac}\n')
4794
4795 networkctl_reload()
95e1fbba 4796 self.wait_online('dummy98:enslaved', 'bond199:degraded')
b448fc0a
YW
4797
4798 output = check_output('ip -d link show bond199')
4799 print(output)
4800 self.assertIn(f'link/ether {mac}', output)
c2990ec3 4801
cc3e488c 4802 def test_bond_operstate(self):
a962d857
YW
4803 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4804 '25-bond99.network', '25-bond-slave.network')
2cf6fdff 4805 start_networkd()
95e1fbba 4806 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
c3a8853f 4807
371810d1 4808 output = check_output('ip -d link show dummy98')
c3a8853f 4809 print(output)
cc3e488c 4810 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
c3a8853f 4811
371810d1 4812 output = check_output('ip -d link show test1')
c3a8853f
YW
4813 print(output)
4814 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
4815
371810d1 4816 output = check_output('ip -d link show bond99')
c3a8853f
YW
4817 print(output)
4818 self.assertRegex(output, 'MASTER,UP,LOWER_UP')
4819
19cf3143
DS
4820 self.wait_operstate('dummy98', 'enslaved')
4821 self.wait_operstate('test1', 'enslaved')
4822 self.wait_operstate('bond99', 'routable')
c3a8853f 4823
cefd6b3d 4824 check_output('ip link set dummy98 down')
c3a8853f 4825
19cf3143
DS
4826 self.wait_operstate('dummy98', 'off')
4827 self.wait_operstate('test1', 'enslaved')
cf4dbd84 4828 self.wait_operstate('bond99', 'routable')
c3a8853f 4829
cefd6b3d 4830 check_output('ip link set dummy98 up')
c3a8853f 4831
19cf3143
DS
4832 self.wait_operstate('dummy98', 'enslaved')
4833 self.wait_operstate('test1', 'enslaved')
4834 self.wait_operstate('bond99', 'routable')
c3a8853f 4835
cefd6b3d
ZJS
4836 check_output('ip link set dummy98 down')
4837 check_output('ip link set test1 down')
cc3e488c 4838
19cf3143
DS
4839 self.wait_operstate('dummy98', 'off')
4840 self.wait_operstate('test1', 'off')
2700d2c7 4841
a4632dc7 4842 if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
2700d2c7
YW
4843 # Huh? Kernel does not recognize that all slave interfaces are down?
4844 # Let's confirm that networkd's operstate is consistent with ip's result.
4845 self.assertNotRegex(output, 'NO-CARRIER')
cc3e488c 4846
be68c2c9 4847class NetworkdBridgeTests(unittest.TestCase, Utilities):
8d17c386 4848
1f0e3109 4849 def setUp(self):
a962d857 4850 setup_common()
1f0e3109
SS
4851
4852 def tearDown(self):
a962d857 4853 tear_down_common()
1f0e3109 4854
9540f8e2
YW
4855 def test_bridge_mac_none(self):
4856 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4857 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4858 start_networkd()
95e1fbba 4859 self.wait_online('dummy98:enslaved', 'bridge99:degraded')
9540f8e2
YW
4860
4861 output = check_output('ip link show dev dummy98')
4862 print(output)
4863 self.assertIn('link/ether 12:34:56:78:9a:01', output)
4864
4865 output = check_output('ip link show dev bridge99')
4866 print(output)
4867 self.assertIn('link/ether 12:34:56:78:9a:01', output)
4868
6f943798 4869 def test_bridge_vlan(self):
a962d857 4870 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
60f4b2c5
YW
4871 '26-bridge.netdev', '26-bridge-vlan-master.network',
4872 copy_dropins=False)
6f943798 4873 start_networkd()
95e1fbba 4874 self.wait_online('test1:enslaved', 'bridge99:degraded')
6f943798
YW
4875
4876 output = check_output('bridge vlan show dev test1')
4877 print(output)
60f4b2c5
YW
4878 # check if the default VID is removed
4879 self.assertNotIn('1 Egress Untagged', output)
4880 for i in range(1000, 3000):
4881 if i == 1010:
4882 self.assertIn(f'{i} PVID', output)
4883 elif i in range(1012, 1016) or i in range(1103, 1109):
4884 self.assertIn(f'{i} Egress Untagged', output)
4885 elif i in range(1008, 1014) or i in range(1100, 1111):
4886 self.assertIn(f'{i}', output)
4887 else:
4888 self.assertNotIn(f'{i}', output)
4889
4890 output = check_output('bridge vlan show dev bridge99')
4891 print(output)
4892 # check if the default VID is removed
4893 self.assertNotIn('1 Egress Untagged', output)
4894 for i in range(1000, 3000):
4895 if i == 1020:
4896 self.assertIn(f'{i} PVID', output)
4897 elif i in range(1022, 1026) or i in range(1203, 1209):
4898 self.assertIn(f'{i} Egress Untagged', output)
4899 elif i in range(1018, 1024) or i in range(1200, 1211):
4900 self.assertIn(f'{i}', output)
4901 else:
4902 self.assertNotIn(f'{i}', output)
4903
4904 # Change vlan IDs
4905 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4906 '26-bridge-vlan-master.network.d/10-override.conf')
4907 networkctl_reload()
95e1fbba 4908 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4909
4910 output = check_output('bridge vlan show dev test1')
4911 print(output)
4912 for i in range(1000, 3000):
4913 if i == 2010:
4914 self.assertIn(f'{i} PVID', output)
4915 elif i in range(2012, 2016) or i in range(2103, 2109):
4916 self.assertIn(f'{i} Egress Untagged', output)
4917 elif i in range(2008, 2014) or i in range(2100, 2111):
4918 self.assertIn(f'{i}', output)
4919 else:
4920 self.assertNotIn(f'{i}', output)
4921
4922 output = check_output('bridge vlan show dev bridge99')
4923 print(output)
4924 for i in range(1000, 3000):
4925 if i == 2020:
4926 self.assertIn(f'{i} PVID', output)
4927 elif i in range(2022, 2026) or i in range(2203, 2209):
4928 self.assertIn(f'{i} Egress Untagged', output)
4929 elif i in range(2018, 2024) or i in range(2200, 2211):
4930 self.assertIn(f'{i}', output)
4931 else:
4932 self.assertNotIn(f'{i}', output)
4933
4934 # Remove several vlan IDs
4935 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4936 '26-bridge-vlan-master.network.d/20-override.conf')
4937 networkctl_reload()
95e1fbba 4938 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4939
4940 output = check_output('bridge vlan show dev test1')
4941 print(output)
4942 for i in range(1000, 3000):
4943 if i == 2010:
4944 self.assertIn(f'{i} PVID', output)
4945 elif i in range(2012, 2016):
4946 self.assertIn(f'{i} Egress Untagged', output)
4947 elif i in range(2008, 2014):
4948 self.assertIn(f'{i}', output)
4949 else:
4950 self.assertNotIn(f'{i}', output)
4951
4952 output = check_output('bridge vlan show dev bridge99')
4953 print(output)
4954 for i in range(1000, 3000):
4955 if i == 2020:
4956 self.assertIn(f'{i} PVID', output)
4957 elif i in range(2022, 2026):
4958 self.assertIn(f'{i} Egress Untagged', output)
4959 elif i in range(2018, 2024):
4960 self.assertIn(f'{i}', output)
4961 else:
4962 self.assertNotIn(f'{i}', output)
4963
4964 # Remove all vlan IDs
4965 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4966 '26-bridge-vlan-master.network.d/30-override.conf')
4967 networkctl_reload()
95e1fbba 4968 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
4969
4970 output = check_output('bridge vlan show dev test1')
4971 print(output)
4972 self.assertNotIn('PVID', output)
4973 for i in range(1000, 3000):
4974 self.assertNotIn(f'{i}', output)
6f943798
YW
4975
4976 output = check_output('bridge vlan show dev bridge99')
4977 print(output)
60f4b2c5
YW
4978 self.assertNotIn('PVID', output)
4979 for i in range(1000, 3000):
4980 self.assertNotIn(f'{i}', output)
6f943798 4981
988b0660 4982 def test_bridge_vlan_issue_20373(self):
a962d857
YW
4983 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4984 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4985 '21-vlan.netdev', '21-vlan.network')
988b0660 4986 start_networkd()
95e1fbba 4987 self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
988b0660
YW
4988
4989 output = check_output('bridge vlan show dev test1')
4990 print(output)
4991 self.assertIn('100 PVID Egress Untagged', output)
4992 self.assertIn('560', output)
4993 self.assertIn('600', output)
4994
4995 output = check_output('bridge vlan show dev bridge99')
4996 print(output)
4997 self.assertIn('1 PVID Egress Untagged', output)
4998 self.assertIn('100', output)
4999 self.assertIn('600', output)
5000
cc0276cc 5001 def test_bridge_mdb(self):
a962d857
YW
5002 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
5003 '26-bridge.netdev', '26-bridge-mdb-master.network')
cc0276cc 5004 start_networkd()
95e1fbba 5005 self.wait_online('test1:enslaved', 'bridge99:degraded')
cc0276cc
YW
5006
5007 output = check_output('bridge mdb show dev bridge99')
5008 print(output)
5009 self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
5010 self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
5011
9f773037 5012 # Old kernel may not support bridge MDB entries on bridge master
a962d857 5013 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
9f773037
YW
5014 self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
5015 self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
5016
b06469a6
YW
5017 def test_bridge_keep_master(self):
5018 check_output('ip link add bridge99 type bridge')
5019 check_output('ip link set bridge99 up')
5020 check_output('ip link add dummy98 type dummy')
5021 check_output('ip link set dummy98 master bridge99')
5022
a962d857 5023 copy_network_unit('23-keep-master.network')
b06469a6 5024 start_networkd()
95e1fbba 5025 self.wait_online('dummy98:enslaved')
b06469a6
YW
5026
5027 output = check_output('ip -d link show dummy98')
5028 print(output)
5029 self.assertRegex(output, 'master bridge99')
5030 self.assertRegex(output, 'bridge')
5031
5032 output = check_output('bridge -d link show dummy98')
5033 print(output)
a962d857
YW
5034 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5035 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5036 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5037 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5038 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
b06469a6 5039 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
5040 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
5041 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
5042 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5043 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
5044 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5045 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
b06469a6 5046
1f0e3109 5047 def test_bridge_property(self):
a962d857
YW
5048 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5049 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5050 '25-bridge99.network')
2cf6fdff 5051 start_networkd()
95e1fbba 5052 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
1f0e3109 5053
21d0ed68
YW
5054 output = check_output('ip -d link show bridge99')
5055 print(output)
5056 self.assertIn('mtu 9000 ', output)
5057
371810d1 5058 output = check_output('ip -d link show test1')
1f0e3109 5059 print(output)
21d0ed68
YW
5060 self.assertIn('master bridge99 ', output)
5061 self.assertIn('bridge_slave', output)
5062 self.assertIn('mtu 9000 ', output)
1f0e3109 5063
371810d1 5064 output = check_output('ip -d link show dummy98')
1f0e3109 5065 print(output)
21d0ed68
YW
5066 self.assertIn('master bridge99 ', output)
5067 self.assertIn('bridge_slave', output)
5068 self.assertIn('mtu 9000 ', output)
1f0e3109 5069
371810d1 5070 output = check_output('ip addr show bridge99')
1f0e3109 5071 print(output)
21d0ed68 5072 self.assertIn('192.168.0.15/24', output)
1f0e3109 5073
371810d1 5074 output = check_output('bridge -d link show dummy98')
1f0e3109 5075 print(output)
a962d857
YW
5076 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5077 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5078 self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
5079 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5080 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5081 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5424fd95 5082 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
5083 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
5084 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
5085 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5086 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
5087 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5088 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4d7ed14f 5089
5424fd95
YW
5090 output = check_output('bridge -d link show test1')
5091 print(output)
a962d857 5092 self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
1f0e3109 5093
371810d1 5094 check_output('ip address add 192.168.0.16/24 dev bridge99')
371810d1 5095 output = check_output('ip addr show bridge99')
2be6c5d2 5096 print(output)
21d0ed68 5097 self.assertIn('192.168.0.16/24', output)
2be6c5d2 5098
e3cbaeab
YW
5099 # for issue #6088
5100 print('### ip -6 route list table all dev bridge99')
5101 output = check_output('ip -6 route list table all dev bridge99')
5102 print(output)
beb75dd3 5103 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 5104
a962d857 5105 remove_link('test1')
cf4dbd84 5106 self.wait_operstate('bridge99', 'routable')
2be6c5d2 5107
21d0ed68
YW
5108 output = check_output('ip -d link show bridge99')
5109 print(output)
5110 self.assertIn('mtu 9000 ', output)
5111
5112 output = check_output('ip -d link show dummy98')
5113 print(output)
5114 self.assertIn('master bridge99 ', output)
5115 self.assertIn('bridge_slave', output)
5116 self.assertIn('mtu 9000 ', output)
804b6cd2 5117
21d0ed68 5118 remove_link('dummy98')
19cf3143 5119 self.wait_operstate('bridge99', 'no-carrier')
2be6c5d2 5120
21d0ed68
YW
5121 output = check_output('ip -d link show bridge99')
5122 print(output)
5123 # When no carrier, the kernel may reset the MTU
5124 self.assertIn('NO-CARRIER', output)
5125
371810d1 5126 output = check_output('ip address show bridge99')
804b6cd2 5127 print(output)
21d0ed68
YW
5128 self.assertNotIn('192.168.0.15/24', output)
5129 self.assertIn('192.168.0.16/24', output) # foreign address is kept
804b6cd2 5130
e3cbaeab
YW
5131 print('### ip -6 route list table all dev bridge99')
5132 output = check_output('ip -6 route list table all dev bridge99')
5133 print(output)
beb75dd3 5134 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 5135
21d0ed68 5136 check_output('ip link add dummy98 type dummy')
95e1fbba 5137 self.wait_online('dummy98:enslaved', 'bridge99:routable')
21d0ed68
YW
5138
5139 output = check_output('ip -d link show bridge99')
5140 print(output)
5141 self.assertIn('mtu 9000 ', output)
5142
5143 output = check_output('ip -d link show dummy98')
5144 print(output)
5145 self.assertIn('master bridge99 ', output)
5146 self.assertIn('bridge_slave', output)
5147 self.assertIn('mtu 9000 ', output)
5148
0fc0d85f 5149 def test_bridge_configure_without_carrier(self):
a962d857
YW
5150 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
5151 '11-dummy.netdev')
0fc0d85f
DS
5152 start_networkd()
5153
5154 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5155 for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
5156 with self.subTest(test=test):
5157 if test == 'no-slave':
5158 # bridge has no slaves; it's up but *might* not have carrier
001c07cf 5159 self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
0fc0d85f
DS
5160 # due to a bug in the kernel, newly-created bridges are brought up
5161 # *with* carrier, unless they have had any setting changed; e.g.
5162 # their mac set, priority set, etc. Then, they will lose carrier
5163 # as soon as a (down) slave interface is added, and regain carrier
5164 # again once the slave interface is brought up.
5165 #self.check_link_attr('bridge99', 'carrier', '0')
5166 elif test == 'add-slave':
5167 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5168 self.check_link_attr('test1', 'operstate', 'down')
5169 check_output('ip link set dev test1 master bridge99')
001c07cf 5170 self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
0fc0d85f
DS
5171 self.check_link_attr('bridge99', 'carrier', '0')
5172 elif test == 'slave-up':
5173 # bring up slave, which will have carrier; bridge gains carrier
5174 check_output('ip link set dev test1 up')
95e1fbba 5175 self.wait_online('bridge99:routable')
0fc0d85f
DS
5176 self.check_link_attr('bridge99', 'carrier', '1')
5177 elif test == 'slave-no-carrier':
5178 # drop slave carrier; bridge loses carrier
5179 check_output('ip link set dev test1 carrier off')
95e1fbba 5180 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
5181 self.check_link_attr('bridge99', 'carrier', '0')
5182 elif test == 'slave-carrier':
5183 # restore slave carrier; bridge gains carrier
5184 check_output('ip link set dev test1 carrier on')
95e1fbba 5185 self.wait_online('bridge99:routable')
0fc0d85f
DS
5186 self.check_link_attr('bridge99', 'carrier', '1')
5187 elif test == 'slave-down':
5188 # bring down slave; bridge loses carrier
5189 check_output('ip link set dev test1 down')
95e1fbba 5190 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
5191 self.check_link_attr('bridge99', 'carrier', '0')
5192
10d670a3 5193 output = networkctl_status('bridge99')
0fc0d85f
DS
5194 self.assertRegex(output, '10.1.2.3')
5195 self.assertRegex(output, '10.1.2.1')
5196
804b6cd2 5197 def test_bridge_ignore_carrier_loss(self):
a962d857
YW
5198 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5199 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5200 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 5201 start_networkd()
95e1fbba 5202 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
804b6cd2 5203
371810d1 5204 check_output('ip address add 192.168.0.16/24 dev bridge99')
a962d857 5205 remove_link('test1', 'dummy98')
804b6cd2
YW
5206 time.sleep(3)
5207
371810d1 5208 output = check_output('ip address show bridge99')
804b6cd2
YW
5209 print(output)
5210 self.assertRegex(output, 'NO-CARRIER')
5211 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5212 self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
5213
6609924c 5214 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
a962d857
YW
5215 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5216 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 5217 start_networkd()
95e1fbba 5218 self.wait_online('bridge99:no-carrier')
6609924c 5219
90e3bcbd
YW
5220 for trial in range(4):
5221 check_output('ip link add dummy98 type dummy')
5222 check_output('ip link set dummy98 up')
5223 if trial < 3:
a962d857 5224 remove_link('dummy98')
6609924c 5225
95e1fbba 5226 self.wait_online('bridge99:routable', 'dummy98:enslaved')
6609924c 5227
371810d1 5228 output = check_output('ip address show bridge99')
6609924c
YW
5229 print(output)
5230 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5231
371810d1 5232 output = check_output('ip rule list table 100')
6609924c 5233 print(output)
c4f7a347 5234 self.assertIn('from all to 8.8.8.8 lookup 100', output)
6609924c 5235
a03ff4c0 5236class NetworkdSRIOVTests(unittest.TestCase, Utilities):
a03ff4c0
YW
5237
5238 def setUp(self):
a962d857 5239 setup_common()
a03ff4c0
YW
5240
5241 def tearDown(self):
a962d857 5242 tear_down_common()
a03ff4c0
YW
5243
5244 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5245 def test_sriov(self):
d8746f16
YW
5246 copy_network_unit('25-default.link', '25-sriov.network')
5247
a962d857 5248 call('modprobe netdevsim')
a03ff4c0 5249
d45476ef 5250 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
a03ff4c0
YW
5251 f.write('99 1')
5252
a962d857 5253 with open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode='w', encoding='utf-8') as f:
a03ff4c0
YW
5254 f.write('3')
5255
a03ff4c0 5256 start_networkd()
95e1fbba 5257 self.wait_online('eni99np1:routable')
a03ff4c0
YW
5258
5259 output = check_output('ip link show dev eni99np1')
5260 print(output)
5261 self.assertRegex(output,
5262 '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 *'
5263 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5264 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5265 )
a03ff4c0 5266
1e8e9730
YW
5267 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5268 def test_sriov_udev(self):
a962d857 5269 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
1e8e9730 5270
a962d857
YW
5271 call('modprobe netdevsim')
5272
d45476ef 5273 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
1e8e9730
YW
5274 f.write('99 1')
5275
5276 start_networkd()
95e1fbba 5277 self.wait_online('eni99np1:routable')
1e8e9730 5278
b95d35b5
YW
5279 # the name eni99np1 may be an alternative name.
5280 ifname = link_resolve('eni99np1')
5281
1e8e9730
YW
5282 output = check_output('ip link show dev eni99np1')
5283 print(output)
5284 self.assertRegex(output,
5285 '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 *'
5286 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5287 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5288 )
1e8e9730
YW
5289 self.assertNotIn('vf 3', output)
5290 self.assertNotIn('vf 4', output)
5291
a962d857 5292 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5293 f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5294
a39a2a81
YW
5295 udevadm_reload()
5296 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5297
5298 output = check_output('ip link show dev eni99np1')
5299 print(output)
5300 self.assertRegex(output,
5301 '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 *'
5302 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5303 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5304 'vf 3'
a962d857 5305 )
1e8e9730
YW
5306 self.assertNotIn('vf 4', output)
5307
a962d857 5308 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5309 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
5310
a39a2a81
YW
5311 udevadm_reload()
5312 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5313
5314 output = check_output('ip link show dev eni99np1')
5315 print(output)
5316 self.assertRegex(output,
5317 '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 *'
5318 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5319 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5320 'vf 3'
a962d857 5321 )
1e8e9730
YW
5322 self.assertNotIn('vf 4', output)
5323
a962d857 5324 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5325 f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5326
a39a2a81
YW
5327 udevadm_reload()
5328 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5329
5330 output = check_output('ip link show dev eni99np1')
5331 print(output)
5332 self.assertRegex(output,
5333 '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 *'
5334 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
a962d857 5335 )
1e8e9730
YW
5336 self.assertNotIn('vf 2', output)
5337 self.assertNotIn('vf 3', output)
5338 self.assertNotIn('vf 4', output)
5339
a962d857 5340 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
5341 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
5342
a39a2a81
YW
5343 udevadm_reload()
5344 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730
YW
5345
5346 output = check_output('ip link show dev eni99np1')
5347 print(output)
5348 self.assertRegex(output,
5349 '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 *'
5350 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5351 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 5352 )
1e8e9730
YW
5353 self.assertNotIn('vf 3', output)
5354 self.assertNotIn('vf 4', output)
5355
be68c2c9 5356class NetworkdLLDPTests(unittest.TestCase, Utilities):
1f0e3109
SS
5357
5358 def setUp(self):
a962d857 5359 setup_common()
1f0e3109
SS
5360
5361 def tearDown(self):
a962d857 5362 tear_down_common()
1f0e3109
SS
5363
5364 def test_lldp(self):
a962d857 5365 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
2cf6fdff 5366 start_networkd()
95e1fbba 5367 self.wait_online('veth99:degraded', 'veth-peer:degraded')
1f0e3109 5368
f0d87798
YW
5369 for trial in range(10):
5370 if trial > 0:
5371 time.sleep(1)
5372
10d670a3 5373 output = networkctl('lldp')
f0d87798 5374 print(output)
0f805b46 5375 if re.search(r'veth99 .* veth-peer .* .......a...', output):
f0d87798
YW
5376 break
5377 else:
5378 self.fail()
1f0e3109 5379
d6360819
YW
5380 # With interface name
5381 output = networkctl('lldp', 'veth99');
5382 print(output)
0f805b46 5383 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
5384
5385 # With interface name pattern
5386 output = networkctl('lldp', 've*9');
5387 print(output)
0f805b46 5388 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
5389
5390 # json format
5391 output = networkctl('--json=short', 'lldp')
5392 print(output)
5393 self.assertIn('"InterfaceName":"veth99"', output)
5394 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 5395 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
5396
5397 # json format with interface name
5398 output = networkctl('--json=short', 'lldp', 'veth99')
5399 print(output)
5400 self.assertIn('"InterfaceName":"veth99"', output)
5401 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 5402 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
5403
5404 # json format with interface name pattern
5405 output = networkctl('--json=short', 'lldp', 've*9')
5406 print(output)
5407 self.assertIn('"InterfaceName":"veth99"', output)
5408 self.assertIn('"PortID":"veth-peer"', output)
0f805b46
YW
5409 self.assertIn('"EnabledCapabilities":128', output)
5410
5411 # LLDP neighbors in status
5412 output = networkctl_status('veth99')
5413 print(output)
5414 self.assertRegex(output, r'Connected To: .* on port veth-peer')
5415
5416 # enable forwarding, to enable the Router flag
5417 with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
5418 f.write('[Network]\nIPv4Forwarding=yes\n')
5419
5420 networkctl_reload()
5421 self.wait_online('veth-peer:degraded')
5422
5423 for trial in range(10):
5424 if trial > 0:
5425 time.sleep(1)
5426
5427 output = networkctl('lldp')
5428 print(output)
5429 if re.search(r'veth99 .* veth-peer .* ....r......', output):
5430 break
5431 else:
5432 self.fail()
5433
5434 # With interface name
5435 output = networkctl('lldp', 'veth99');
5436 print(output)
5437 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
5438
5439 # With interface name pattern
5440 output = networkctl('lldp', 've*9');
5441 print(output)
5442 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
5443
5444 # json format
5445 output = networkctl('--json=short', 'lldp')
5446 print(output)
5447 self.assertIn('"InterfaceName":"veth99"', output)
5448 self.assertIn('"PortID":"veth-peer"', output)
5449 self.assertIn('"EnabledCapabilities":16', output)
5450
5451 # json format with interface name
5452 output = networkctl('--json=short', 'lldp', 'veth99')
5453 print(output)
5454 self.assertIn('"InterfaceName":"veth99"', output)
5455 self.assertIn('"PortID":"veth-peer"', output)
5456 self.assertIn('"EnabledCapabilities":16', output)
5457
5458 # json format with interface name pattern
5459 output = networkctl('--json=short', 'lldp', 've*9')
5460 print(output)
5461 self.assertIn('"InterfaceName":"veth99"', output)
5462 self.assertIn('"PortID":"veth-peer"', output)
5463 self.assertIn('"EnabledCapabilities":16', output)
d6360819
YW
5464
5465 # LLDP neighbors in status
5466 output = networkctl_status('veth99')
5467 print(output)
5468 self.assertRegex(output, r'Connected To: .* on port veth-peer')
5469
be68c2c9 5470class NetworkdRATests(unittest.TestCase, Utilities):
1f0e3109
SS
5471
5472 def setUp(self):
a962d857 5473 setup_common()
1f0e3109
SS
5474
5475 def tearDown(self):
a962d857 5476 tear_down_common()
1f0e3109
SS
5477
5478 def test_ipv6_prefix_delegation(self):
a962d857 5479 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
c742d7e8
TM
5480 self.setup_nftset('addr6', 'ipv6_addr')
5481 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5482 self.setup_nftset('ifindex', 'iface_index')
2cf6fdff 5483 start_networkd()
95e1fbba 5484 self.wait_online('veth99:routable', 'veth-peer:degraded')
1f0e3109 5485
3976c430
YW
5486 # IPv6SendRA=yes implies IPv6Forwarding.
5487 self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5488
10d670a3 5489 output = resolvectl('dns', 'veth99')
41fd8fe7
YW
5490 print(output)
5491 self.assertRegex(output, 'fe80::')
5492 self.assertRegex(output, '2002:da8:1::1')
5493
10d670a3 5494 output = resolvectl('domain', 'veth99')
80762ccc
YW
5495 print(output)
5496 self.assertIn('hogehoge.test', output)
5497
10d670a3 5498 output = networkctl_status('veth99')
1f0e3109
SS
5499 print(output)
5500 self.assertRegex(output, '2002:da8:1:0')
5501
a4640bed
TM
5502 self.check_netlabel('veth99', '2002:da8:1::/64')
5503 self.check_netlabel('veth99', '2002:da8:2::/64')
5504
c742d7e8
TM
5505 self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5506 self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5507 self.check_nftset('network6', '2002:da8:1::/64')
5508 self.check_nftset('network6', '2002:da8:2::/64')
5509 self.check_nftset('ifindex', 'veth99')
5510
5511 self.teardown_nftset('addr6', 'network6', 'ifindex')
5512
6d1cea7b 5513 def check_ipv6_token_static(self):
95e1fbba 5514 self.wait_online('veth99:routable', 'veth-peer:degraded')
b241fa00 5515
10d670a3 5516 output = networkctl_status('veth99')
b241fa00
KF
5517 print(output)
5518 self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
68248f43
YW
5519 self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
5520 self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
5521 self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
b241fa00 5522
6d1cea7b
YW
5523 def test_ipv6_token_static(self):
5524 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5525 start_networkd()
5526
5527 self.check_ipv6_token_static()
5528
5529 for _ in range(20):
5530 check_output('ip link set veth99 down')
5531 check_output('ip link set veth99 up')
5532
5533 self.check_ipv6_token_static()
5534
5535 for _ in range(20):
5536 check_output('ip link set veth99 down')
5537 time.sleep(random.uniform(0, 0.1))
5538 check_output('ip link set veth99 up')
5539 time.sleep(random.uniform(0, 0.1))
5540
5541 self.check_ipv6_token_static()
5542
9dcdf16b
YW
5543 def test_ndisc_redirect(self):
5544 if not os.path.exists(test_ndisc_send):
5545 self.skipTest(f"{test_ndisc_send} does not exist.")
5546
5547 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5548 start_networkd()
5549
5550 self.check_ipv6_token_static()
5551
5552 # Introduce two redirect routes.
5553 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')
5554 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::1 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5555 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5556 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 proto redirect', ipv='-6', timeout_sec=10)
5557
5558 # Change the target address of the redirects.
5559 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::2 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
5560 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::3 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5561 self.wait_route_dropped('veth99', r'2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5562 self.wait_route_dropped('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 proto redirect', ipv='-6', timeout_sec=10)
5563 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d via 2002:da8:1::2 proto redirect', ipv='-6', timeout_sec=10)
5564 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::3 proto redirect', ipv='-6', timeout_sec=10)
5565
5566 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
5567 # Then, verify that all redirect routes and the default route are dropped.
5568 output = check_output('ip -6 address show dev veth-peer scope link')
5569 veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
5570 print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
5571 check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
5572 self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
5573 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
5574
a0430b0d
YW
5575 def check_ndisc_mtu(self, mtu):
5576 for _ in range(20):
5577 output = read_ipv6_sysctl_attr('veth99', 'mtu')
5578 if output == f'{mtu}':
5579 break
5580 time.sleep(0.5)
5581 else:
5582 self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
5583
5584 def test_ndisc_mtu(self):
5585 if not os.path.exists(test_ndisc_send):
5586 self.skipTest(f"{test_ndisc_send} does not exist.")
5587
5588 copy_network_unit('25-veth.netdev',
5589 '25-veth-peer-no-address.network',
5590 '25-ipv6-prefix-veth-token-static.network')
5591 start_networkd()
5592 self.wait_online('veth-peer:degraded')
5593
5594 for _ in range(20):
5595 output = read_networkd_log()
5596 if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output:
5597 break
5598 time.sleep(0.5)
5599 else:
5600 self.fail('sd-ndisc does not started on veth99.')
5601
5602 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
5603 self.check_ndisc_mtu(1400)
5604
5605 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
5606 self.check_ndisc_mtu(1410)
5607
5608 check_output('ip link set dev veth99 mtu 1600')
5609 self.check_ndisc_mtu(1410)
5610
5611 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
5612 self.check_ndisc_mtu(1600)
5613
5614 check_output('ip link set dev veth99 mtu 1800')
5615 self.check_ndisc_mtu(1700)
5616
68248f43 5617 def test_ipv6_token_prefixstable(self):
a962d857 5618 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
c24c83dc 5619 start_networkd()
95e1fbba 5620 self.wait_online('veth99:routable', 'veth-peer:degraded')
c24c83dc 5621
ce4ed0ad 5622 output = check_output('ip -6 address show dev veth99')
c24c83dc 5623 print(output)
ce4ed0ad
YW
5624 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5625 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
5626
5627 with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
5628 f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
5629
5630 networkctl_reload()
5631 self.wait_online('veth99:routable')
5632
5633 output = check_output('ip -6 address show dev veth99')
5634 print(output)
5635 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5636 self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
5637
5638 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
5639 check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
5640
5641 networkctl_reconfigure('veth99')
5642 self.wait_online('veth99:routable')
5643
5644 output = check_output('ip -6 address show dev veth99')
5645 print(output)
5646 self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5647 self.assertIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
5648
5649 check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
5650 check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
5651
5652 networkctl_reconfigure('veth99')
5653 self.wait_online('veth99:routable')
5654
5655 output = check_output('ip -6 address show dev veth99')
5656 print(output)
5657 self.assertNotIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
5658 self.assertNotIn('2002:da8:1:0:da5d:e50a:43fd:5d0f/64', output) # the 2nd prefixstable
5659 self.assertIn('2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', output) # the 3rd prefixstable
c24c83dc 5660
68248f43 5661 def test_ipv6_token_prefixstable_without_address(self):
a962d857 5662 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
87bbebea 5663 start_networkd()
95e1fbba 5664 self.wait_online('veth99:routable', 'veth-peer:degraded')
87bbebea 5665
10d670a3 5666 output = networkctl_status('veth99')
87bbebea 5667 print(output)
7a2e124b
YW
5668 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
5669 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
87bbebea 5670
29fbbb13
YW
5671 def check_router_hop_limit(self, hop_limit):
5672 self.wait_route('client', rf'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv='-6', timeout_sec=10)
5673
5674 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5675 print(output)
5676 self.assertIn(f'hoplimit {hop_limit}', output)
5677
5678 self.check_ipv6_sysctl_attr('client', 'hop_limit', f'{hop_limit}')
5679
5680 def test_router_hop_limit(self):
5681 copy_network_unit('25-veth-client.netdev',
5682 '25-veth-router.netdev',
5683 '26-bridge.netdev',
5684 '25-veth-bridge.network',
5685 '25-veth-client.network',
5686 '25-veth-router-hop-limit.network',
5687 '25-bridge99.network')
5688 start_networkd()
5689 self.wait_online('client-p:enslaved',
5690 'router:degraded', 'router-p:enslaved',
5691 'bridge99:routable')
5692
5693 self.check_router_hop_limit(42)
5694
5695 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
5696 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
5697
5698 networkctl_reload()
5699
5700 self.check_router_hop_limit(43)
5701
bb80f633
YW
5702 def test_router_preference(self):
5703 copy_network_unit('25-veth-client.netdev',
5704 '25-veth-router-high.netdev',
5705 '25-veth-router-low.netdev',
5706 '26-bridge.netdev',
5707 '25-veth-bridge.network',
5708 '25-veth-client.network',
5709 '25-veth-router-high.network',
5710 '25-veth-router-low.network',
5711 '25-bridge99.network')
5712 start_networkd()
95e1fbba
YW
5713 self.wait_online('client-p:enslaved',
5714 'router-high:degraded', 'router-high-p:enslaved',
5715 'router-low:degraded', 'router-low-p:enslaved',
5716 'bridge99:routable')
bb80f633
YW
5717
5718 networkctl_reconfigure('client')
95e1fbba 5719 self.wait_online('client:routable')
bb80f633
YW
5720
5721 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5722 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5723 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv='-6', timeout_sec=10)
5724 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv='-6', timeout_sec=10)
5725
5726 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5727 print(output)
9fbab82b 5728 self.assertIn('metric 512', output)
bb80f633
YW
5729 self.assertIn('pref high', output)
5730 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5731 print(output)
9fbab82b 5732 self.assertIn('metric 2048', output)
bb80f633
YW
5733 self.assertIn('pref low', output)
5734
5735 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
5736 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5737
5738 networkctl_reload()
95e1fbba 5739 self.wait_online('client:routable')
bb80f633
YW
5740
5741 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5742 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5743 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv='-6', timeout_sec=10)
5744 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv='-6', timeout_sec=10)
5745
5746 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5747 print(output)
9fbab82b
YW
5748 self.assertIn('metric 100', output)
5749 self.assertNotIn('metric 512', output)
bb80f633
YW
5750 self.assertIn('pref high', output)
5751 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5752 print(output)
9fbab82b
YW
5753 self.assertIn('metric 300', output)
5754 self.assertNotIn('metric 2048', output)
bb80f633
YW
5755 self.assertIn('pref low', output)
5756
9fbab82b
YW
5757 # swap the preference (for issue #28439)
5758 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5759 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5760 networkctl_reload()
5761 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv='-6', timeout_sec=10)
5762 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', 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)
5766 self.assertIn('metric 300', output)
5767 self.assertNotIn('metric 100', output)
5768 self.assertIn('pref low', output)
5769 self.assertNotIn('pref high', output)
5770 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5771 print(output)
5772 self.assertIn('metric 100', output)
5773 self.assertNotIn('metric 300', output)
5774 self.assertIn('pref high', output)
5775 self.assertNotIn('pref low', output)
5776
c1dd58b3
FS
5777 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5778 def test_captive_portal(self):
5779 copy_network_unit('25-veth-client.netdev',
5780 '25-veth-router-captive.netdev',
5781 '26-bridge.netdev',
5782 '25-veth-client-captive.network',
5783 '25-veth-router-captive.network',
5784 '25-veth-bridge-captive.network',
5785 '25-bridge99.network')
5786 start_networkd()
95e1fbba
YW
5787 self.wait_online('bridge99:routable', 'client-p:enslaved',
5788 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
5789
5790 start_radvd(config_file='captive-portal.conf')
5791 networkctl_reconfigure('client')
95e1fbba 5792 self.wait_online('client:routable')
c1dd58b3
FS
5793
5794 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 5795 output = networkctl_status('client')
c1dd58b3
FS
5796 print(output)
5797 self.assertIn('Captive Portal: http://systemd.io', output)
5798
5799 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5800 def test_invalid_captive_portal(self):
5801 def radvd_write_config(captive_portal_uri):
5802 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
5803 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5804
5805 captive_portal_uris = [
5806 "42ěščěškd ěšč ě s",
5807 " ",
5808 "🤔",
5809 ]
5810
5811 copy_network_unit('25-veth-client.netdev',
5812 '25-veth-router-captive.netdev',
5813 '26-bridge.netdev',
5814 '25-veth-client-captive.network',
5815 '25-veth-router-captive.network',
5816 '25-veth-bridge-captive.network',
5817 '25-bridge99.network')
5818 start_networkd()
95e1fbba
YW
5819 self.wait_online('bridge99:routable', 'client-p:enslaved',
5820 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
5821
5822 for uri in captive_portal_uris:
5823 print(f"Captive portal: {uri}")
5824 radvd_write_config(uri)
5825 stop_radvd()
5826 start_radvd(config_file='bogus-captive-portal.conf')
5827 networkctl_reconfigure('client')
95e1fbba 5828 self.wait_online('client:routable')
c1dd58b3
FS
5829
5830 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 5831 output = networkctl_status('client')
c1dd58b3
FS
5832 print(output)
5833 self.assertNotIn('Captive Portal:', output)
5834
be68c2c9 5835class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
1f0e3109
SS
5836
5837 def setUp(self):
a962d857 5838 setup_common()
1f0e3109
SS
5839
5840 def tearDown(self):
a962d857 5841 tear_down_common()
1f0e3109 5842
bc91875a 5843 def check_dhcp_server(self, persist_leases=True):
10d670a3 5844 output = networkctl_status('veth99')
c5f7a087 5845 print(output)
283863a1 5846 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
c5f7a087
YW
5847 self.assertIn('Gateway: 192.168.5.3', output)
5848 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
5849 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
5850
10d670a3 5851 output = networkctl_status('veth-peer')
1c4411b7 5852 print(output)
8bdece74
FS
5853 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
5854
bc91875a
YW
5855 if persist_leases:
5856 with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
5857 check_json(f.read())
5858 else:
5859 self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
5860
5861 def test_dhcp_server(self):
5862 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5863 start_networkd()
5864 self.wait_online('veth99:routable', 'veth-peer:routable')
5865
5866 self.check_dhcp_server()
5867
1c4411b7
YW
5868 networkctl_reconfigure('veth-peer')
5869 self.wait_online('veth-peer:routable')
5870
5871 for _ in range(10):
5872 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
5873 if 'Offered DHCP leases: 192.168.5.' in output:
5874 break
5875 time.sleep(.2)
5876 else:
5877 self.fail()
5878
bc91875a
YW
5879 def test_dhcp_server_persist_leases_no(self):
5880 copy_networkd_conf_dropin('persist-leases-no.conf')
5881 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5882 start_networkd()
5883 self.wait_online('veth99:routable', 'veth-peer:routable')
5884
5885 self.check_dhcp_server(persist_leases=False)
5886
5887 remove_networkd_conf_dropin('persist-leases-no.conf')
5888 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
5889 f.write('[DHCPServer]\nPersistLeases=no')
5890 restart_networkd()
5891 self.wait_online('veth99:routable', 'veth-peer:routable')
5892
5893 self.check_dhcp_server(persist_leases=False)
5894
47f1ce16
YW
5895 def test_dhcp_server_null_server_address(self):
5896 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5897 start_networkd()
95e1fbba 5898 self.wait_online('veth99:routable', 'veth-peer:routable')
47f1ce16
YW
5899
5900 output = check_output('ip --json address show dev veth-peer')
5901 server_address = json.loads(output)[0]['addr_info'][0]['local']
5902 print(server_address)
5903
5904 output = check_output('ip --json address show dev veth99')
5905 client_address = json.loads(output)[0]['addr_info'][0]['local']
5906 print(client_address)
5907
10d670a3 5908 output = networkctl_status('veth99')
47f1ce16
YW
5909 print(output)
5910 self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
5911 self.assertIn(f'Gateway: {server_address}', output)
5912 self.assertIn(f'DNS: {server_address}', output)
5913 self.assertIn(f'NTP: {server_address}', output)
5914
10d670a3 5915 output = networkctl_status('veth-peer')
47f1ce16
YW
5916 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5917
93126bb0
YW
5918 # Check if the same addresses are used even if the service is restarted.
5919 restart_networkd()
5920 self.wait_online('veth99:routable', 'veth-peer:routable')
5921
5922 output = check_output('ip -4 address show dev veth-peer')
5923 print(output)
5924 self.assertIn(f'{server_address}', output)
5925
5926 output = check_output('ip -4 address show dev veth99')
5927 print(output)
5928 self.assertIn(f'{client_address}', output)
5929
5930 output = networkctl_status('veth99')
5931 print(output)
5932 self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
5933 self.assertIn(f'Gateway: {server_address}', output)
5934 self.assertIn(f'DNS: {server_address}', output)
5935 self.assertIn(f'NTP: {server_address}', output)
5936
5937 output = networkctl_status('veth-peer')
5938 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5939
c5f7a087 5940 def test_dhcp_server_with_uplink(self):
a962d857
YW
5941 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5942 '12-dummy.netdev', '25-dhcp-server-uplink.network')
2cf6fdff 5943 start_networkd()
95e1fbba 5944 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 5945
10d670a3 5946 output = networkctl_status('veth99')
1f0e3109 5947 print(output)
283863a1 5948 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
32d97330
YW
5949 self.assertIn('Gateway: 192.168.5.3', output)
5950 self.assertIn('DNS: 192.168.5.1', output)
5951 self.assertIn('NTP: 192.168.5.1', output)
1f0e3109 5952
1f0e3109 5953 def test_emit_router_timezone(self):
a962d857 5954 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
2cf6fdff 5955 start_networkd()
95e1fbba 5956 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 5957
10d670a3 5958 output = networkctl_status('veth99')
1f0e3109 5959 print(output)
283863a1 5960 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
32d97330
YW
5961 self.assertIn('Gateway: 192.168.5.1', output)
5962 self.assertIn('Time Zone: Europe/Berlin', output)
1f0e3109 5963
ffaece68 5964 def test_dhcp_server_static_lease(self):
a962d857 5965 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
ffaece68 5966 start_networkd()
95e1fbba 5967 self.wait_online('veth99:routable', 'veth-peer:routable')
ffaece68 5968
10d670a3 5969 output = networkctl_status('veth99')
ffaece68 5970 print(output)
32d97330 5971 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
82c60c93
YW
5972 self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output)
5973
5974 def test_dhcp_server_static_lease_default_client_id(self):
5975 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5976 start_networkd()
95e1fbba 5977 self.wait_online('veth99:routable', 'veth-peer:routable')
82c60c93 5978
10d670a3 5979 output = networkctl_status('veth99')
82c60c93
YW
5980 print(output)
5981 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
5982 self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
ffaece68 5983
c95df587 5984class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
c95df587
YA
5985
5986 def setUp(self):
a962d857 5987 setup_common()
c95df587
YA
5988
5989 def tearDown(self):
a962d857 5990 tear_down_common()
c95df587
YA
5991
5992 def test_relay_agent(self):
a962d857
YW
5993 copy_network_unit('25-agent-veth-client.netdev',
5994 '25-agent-veth-server.netdev',
5995 '25-agent-client.network',
5996 '25-agent-server.network',
5997 '25-agent-client-peer.network',
5998 '25-agent-server-peer.network')
c95df587
YA
5999 start_networkd()
6000
95e1fbba 6001 self.wait_online('client:routable')
c95df587 6002
10d670a3 6003 output = networkctl_status('client')
c95df587 6004 print(output)
283863a1 6005 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
c95df587 6006
c4047829 6007 def test_relay_agent_on_bridge(self):
a663ddc0
YW
6008 copy_network_unit('25-agent-bridge.netdev',
6009 '25-agent-veth-client.netdev',
6010 '25-agent-bridge.network',
6011 '25-agent-bridge-port.network',
6012 '25-agent-client.network')
6013 start_networkd()
95e1fbba 6014 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
a663ddc0
YW
6015
6016 # For issue #30763.
6017 expect = 'bridge-relay: DHCPv4 server: STARTED'
6018 for _ in range(20):
6019 if expect in read_networkd_log():
6020 break
6021 time.sleep(0.5)
6022 else:
6023 self.fail()
6024
be68c2c9 6025class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
1f0e3109
SS
6026
6027 def setUp(self):
a962d857 6028 setup_common()
1f0e3109
SS
6029
6030 def tearDown(self):
a962d857 6031 tear_down_common()
1f0e3109
SS
6032
6033 def test_dhcp_client_ipv6_only(self):
a962d857 6034 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
1f0e3109 6035
2cf6fdff 6036 start_networkd()
95e1fbba 6037 self.wait_online('veth-peer:carrier')
2d7ca6b4
YW
6038
6039 # information request mode
46f2eb51
YW
6040 # The name ipv6-only option may not be supported by older dnsmasq
6041 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
6042 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6043 '--dhcp-option=option6:dns-server,[2600::ee]',
2d7ca6b4
YW
6044 '--dhcp-option=option6:ntp-server,[2600::ff]',
6045 ra_mode='ra-stateless')
95e1fbba 6046 self.wait_online('veth99:routable', 'veth-peer:routable')
2d7ca6b4 6047
0f9efffa
YW
6048 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
6049 # Let's wait for the expected DNS server being listed in the state file.
6050 for _ in range(100):
6051 output = read_link_state_file('veth99')
0f9efffa
YW
6052 if 'DNS=2600::ee' in output:
6053 break
6054 time.sleep(.2)
6055
2d7ca6b4
YW
6056 # Check link state file
6057 print('## link state file')
6058 output = read_link_state_file('veth99')
6059 print(output)
6060 self.assertIn('DNS=2600::ee', output)
6061 self.assertIn('NTP=2600::ff', output)
6062
6063 # Check manager state file
6064 print('## manager state file')
6065 output = read_manager_state_file()
6066 print(output)
6067 self.assertRegex(output, 'DNS=.*2600::ee')
6068 self.assertRegex(output, 'NTP=.*2600::ff')
6069
6070 print('## dnsmasq log')
6071 output = read_dnsmasq_log_file()
6072 print(output)
6073 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
6074 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
6075 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6076 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6077 self.assertNotIn('DHCPREPLY(veth-peer)', output)
6078
3aa47694 6079 # Check json format
10d670a3 6080 check_json(networkctl_json('veth99'))
3aa47694 6081
2d7ca6b4
YW
6082 # solicit mode
6083 stop_dnsmasq()
46f2eb51
YW
6084 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6085 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 6086 '--dhcp-option=option6:ntp-server,[2600::ff]')
2d7ca6b4 6087 networkctl_reconfigure('veth99')
95e1fbba 6088 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 6089
18f2638f
YW
6090 # checking address
6091 output = check_output('ip address show dev veth99 scope global')
c5fcd8a7 6092 print(output)
18f2638f
YW
6093 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6094 self.assertNotIn('192.168.5', output)
c5fcd8a7 6095
589af70b
YW
6096 # checking semi-static route
6097 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6098 print(output)
6099 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6100
3a956d38 6101 # Confirm that ipv6 token is not set in the kernel
371810d1 6102 output = check_output('ip token show dev veth99')
3a956d38
YW
6103 print(output)
6104 self.assertRegex(output, 'token :: dev veth99')
6105
0f9efffa 6106 # Make manager and link state file updated
10d670a3 6107 resolvectl('revert', 'veth99')
0f9efffa 6108
34290c6a
YW
6109 # Check link state file
6110 print('## link state file')
6111 output = read_link_state_file('veth99')
6112 print(output)
6113 self.assertIn('DNS=2600::ee', output)
6114 self.assertIn('NTP=2600::ff', output)
6115
6116 # Check manager state file
6117 print('## manager state file')
6118 output = read_manager_state_file()
6119 print(output)
6120 self.assertRegex(output, 'DNS=.*2600::ee')
6121 self.assertRegex(output, 'NTP=.*2600::ff')
6122
91a7afde
YW
6123 print('## dnsmasq log')
6124 output = read_dnsmasq_log_file()
6125 print(output)
2d7ca6b4 6126 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
6127 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6128 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6129 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6130 self.assertIn('DHCPREPLY(veth-peer)', output)
6131 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6132
3aa47694 6133 # Check json format
10d670a3 6134 check_json(networkctl_json('veth99'))
3aa47694
YW
6135
6136 # Testing without rapid commit support
91a7afde
YW
6137 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
6138 f.write('\n[DHCPv6]\nRapidCommit=no\n')
6139
6140 stop_dnsmasq()
46f2eb51
YW
6141 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6142 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 6143 '--dhcp-option=option6:ntp-server,[2600::ff]')
91a7afde
YW
6144
6145 networkctl_reload()
95e1fbba 6146 self.wait_online('veth99:routable', 'veth-peer:routable')
91a7afde
YW
6147
6148 # checking address
6149 output = check_output('ip address show dev veth99 scope global')
6150 print(output)
6151 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6152 self.assertNotIn('192.168.5', output)
6153
6154 # checking semi-static route
6155 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6156 print(output)
6157 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6158
0f9efffa 6159 # Make manager and link state file updated
10d670a3 6160 resolvectl('revert', 'veth99')
0f9efffa 6161
34290c6a
YW
6162 # Check link state file
6163 print('## link state file')
6164 output = read_link_state_file('veth99')
6165 print(output)
6166 self.assertIn('DNS=2600::ee', output)
6167 self.assertIn('NTP=2600::ff', output)
6168
6169 # Check manager state file
6170 print('## manager state file')
6171 output = read_manager_state_file()
6172 print(output)
6173 self.assertRegex(output, 'DNS=.*2600::ee')
6174 self.assertRegex(output, 'NTP=.*2600::ff')
6175
91a7afde
YW
6176 print('## dnsmasq log')
6177 output = read_dnsmasq_log_file()
6178 print(output)
2d7ca6b4 6179 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
6180 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6181 self.assertIn('DHCPADVERTISE(veth-peer)', output)
6182 self.assertIn('DHCPREQUEST(veth-peer)', output)
6183 self.assertIn('DHCPREPLY(veth-peer)', output)
6184 self.assertNotIn('rapid-commit', output)
6185
3aa47694 6186 # Check json format
10d670a3 6187 check_json(networkctl_json('veth99'))
3aa47694 6188
e1ef7771 6189 def test_dhcp_client_ipv6_dbus_status(self):
e1ef7771 6190 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
e1ef7771 6191 start_networkd()
95e1fbba 6192 self.wait_online('veth-peer:carrier')
e1ef7771 6193
6194 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
fcdd21ec 6195 # bit set) has yet been received and the configuration does not include WithoutRA=true
e081ffc1 6196 state = get_dhcp6_client_state('veth99')
46f2eb51 6197 print(f"DHCPv6 client state = {state}")
e1ef7771 6198 self.assertEqual(state, 'stopped')
6199
46f2eb51
YW
6200 state = get_dhcp4_client_state('veth99')
6201 print(f"DHCPv4 client state = {state}")
6202 self.assertEqual(state, 'selecting')
6203
6204 start_dnsmasq('--dhcp-option=108,00:00:02:00')
95e1fbba 6205 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 6206
e081ffc1 6207 state = get_dhcp6_client_state('veth99')
46f2eb51 6208 print(f"DHCPv6 client state = {state}")
e1ef7771 6209 self.assertEqual(state, 'bound')
6210
46f2eb51
YW
6211 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
6212 for _ in range(100):
6213 state = get_dhcp4_client_state('veth99')
6214 if state == 'stopped':
6215 break
6216 time.sleep(.2)
6217
6218 print(f"DHCPv4 client state = {state}")
6219 self.assertEqual(state, 'stopped')
6220
aa7336f1
YW
6221 # restart dnsmasq to clear log
6222 stop_dnsmasq()
6223 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6224
6225 # Test renew command
6226 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
10d670a3 6227 networkctl('renew', 'veth99')
aa7336f1
YW
6228
6229 for _ in range(100):
6230 state = get_dhcp4_client_state('veth99')
6231 if state == 'stopped':
6232 break
6233 time.sleep(.2)
6234
6235 print(f"DHCPv4 client state = {state}")
6236 self.assertEqual(state, 'stopped')
6237
6238 print('## dnsmasq log')
6239 output = read_dnsmasq_log_file()
6240 print(output)
6241 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
6242 self.assertIn('DHCPOFFER(veth-peer)', output)
6243 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6244 self.assertNotIn('DHCPACK(veth-peer)', output)
6245
e448fcd0
SS
6246 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
6247 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
6248
6249 start_networkd()
95e1fbba 6250 self.wait_online('veth-peer:carrier')
e448fcd0 6251 start_dnsmasq()
95e1fbba 6252 self.wait_online('veth99:routable', 'veth-peer:routable')
e448fcd0
SS
6253
6254 # checking address
6255 output = check_output('ip address show dev veth99 scope global')
6256 print(output)
6257 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6258 self.assertNotIn('192.168.5', output)
6259
6260 print('## dnsmasq log')
6261 output = read_dnsmasq_log_file()
6262 print(output)
6263 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
6264 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6265 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6266 self.assertIn('DHCPREPLY(veth-peer)', output)
6267 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6268
1f0e3109 6269 def test_dhcp_client_ipv4_only(self):
a962d857 6270 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
1f0e3109 6271
c742d7e8
TM
6272 self.setup_nftset('addr4', 'ipv4_addr')
6273 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6274 self.setup_nftset('ifindex', 'iface_index')
6275
2cf6fdff 6276 start_networkd()
95e1fbba 6277 self.wait_online('veth-peer:carrier')
a962d857 6278 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
b5c8f471 6279 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
a962d857
YW
6280 '--dhcp-option=option:domain-search,example.com',
6281 '--dhcp-alternate-port=67,5555',
6282 ipv4_range='192.168.5.110,192.168.5.119')
95e1fbba 6283 self.wait_online('veth99:routable', 'veth-peer:routable')
9e7d91ed 6284 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
1f0e3109 6285
18f2638f
YW
6286 print('## ip address show dev veth99 scope global')
6287 output = check_output('ip address show dev veth99 scope global')
1f0e3109 6288 print(output)
18f2638f
YW
6289 self.assertIn('mtu 1492', output)
6290 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6291 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')
6292 self.assertNotIn('2600::', output)
195a18c1 6293
6b524d70
YW
6294 output = check_output('ip -4 --json address show dev veth99')
6295 for i in json.loads(output)[0]['addr_info']:
6296 if i['label'] == 'test-label':
6297 address1 = i['local']
6298 break
6299 else:
6300 self.assertFalse(True)
6301
6302 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
6303
18f2638f
YW
6304 print('## ip route show table main dev veth99')
6305 output = check_output('ip route show table main dev veth99')
195a18c1 6306 print(output)
18f2638f
YW
6307 # no DHCP routes assigned to the main table
6308 self.assertNotIn('proto dhcp', output)
6309 # static routes
6310 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6311 self.assertIn('192.168.5.0/24 proto static scope link', output)
6312 self.assertIn('192.168.6.0/24 proto static scope link', output)
6313 self.assertIn('192.168.7.0/24 proto static scope link', output)
6314
6315 print('## ip route show table 211 dev veth99')
6316 output = check_output('ip route show table 211 dev veth99')
6317 print(output)
6b524d70
YW
6318 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6319 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6320 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6321 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6322 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
18f2638f
YW
6323 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6324
1a8e1d78
YW
6325 print('## link state file')
6326 output = read_link_state_file('veth99')
6327 print(output)
b5c8f471 6328 # checking DNS server, SIP server, and Domains
1a8e1d78 6329 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
b5c8f471 6330 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
1a8e1d78 6331 self.assertIn('DOMAINS=example.com', output)
195a18c1 6332
b5c8f471 6333 print('## json')
10d670a3 6334 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
6335
6336 self.assertEqual(len(j['DNS']), 2)
6337 for i in j['DNS']:
6338 print(i)
6339 self.assertEqual(i['Family'], 2)
6340 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6341 self.assertRegex(a, '^192.168.5.[67]$')
6342 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6343 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6344 self.assertEqual('192.168.5.1', a)
6345
6346 self.assertEqual(len(j['SIP']), 2)
6347 for i in j['SIP']:
6348 print(i)
6349 self.assertEqual(i['Family'], 2)
6350 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6351 self.assertRegex(a, '^192.168.5.2[12]$')
6352 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6353 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6354 self.assertEqual('192.168.5.1', a)
6355
18f2638f 6356 print('## dnsmasq log')
063c7e9b
YW
6357 output = read_dnsmasq_log_file()
6358 print(output)
6359 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 6360 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
063c7e9b
YW
6361 self.assertIn('client provides name: test-hostname', output)
6362 self.assertIn('26:mtu', output)
18f2638f
YW
6363
6364 # change address range, DNS servers, and Domains
888f57c1 6365 stop_dnsmasq()
a962d857 6366 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
b5c8f471 6367 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
a962d857
YW
6368 '--dhcp-option=option:domain-search,foo.example.com',
6369 '--dhcp-alternate-port=67,5555',
6370 ipv4_range='192.168.5.120,192.168.5.129',)
195a18c1
YW
6371
6372 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c 6373 print('Wait for the DHCP lease to be expired')
6b524d70 6374 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
4b31fc88 6375 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
195a18c1 6376
95e1fbba 6377 self.wait_online('veth99:routable', 'veth-peer:routable')
195a18c1 6378
18f2638f
YW
6379 print('## ip address show dev veth99 scope global')
6380 output = check_output('ip address show dev veth99 scope global')
195a18c1 6381 print(output)
18f2638f
YW
6382 self.assertIn('mtu 1492', output)
6383 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6b524d70 6384 self.assertNotIn(f'{address1}', output)
18f2638f
YW
6385 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')
6386 self.assertNotIn('2600::', output)
195a18c1 6387
6b524d70
YW
6388 output = check_output('ip -4 --json address show dev veth99')
6389 for i in json.loads(output)[0]['addr_info']:
6390 if i['label'] == 'test-label':
6391 address2 = i['local']
6392 break
6393 else:
6394 self.assertFalse(True)
6395
6396 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
6397
18f2638f
YW
6398 print('## ip route show table main dev veth99')
6399 output = check_output('ip route show table main dev veth99')
195a18c1 6400 print(output)
18f2638f
YW
6401 # no DHCP routes assigned to the main table
6402 self.assertNotIn('proto dhcp', output)
6403 # static routes
6404 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6405 self.assertIn('192.168.5.0/24 proto static scope link', output)
6406 self.assertIn('192.168.6.0/24 proto static scope link', output)
6407 self.assertIn('192.168.7.0/24 proto static scope link', output)
6408
6409 print('## ip route show table 211 dev veth99')
6410 output = check_output('ip route show table 211 dev veth99')
6411 print(output)
6b524d70
YW
6412 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6413 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6414 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
18f2638f 6415 self.assertNotIn('192.168.5.6', output)
6b524d70
YW
6416 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6417 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
18f2638f
YW
6418 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6419
1a8e1d78
YW
6420 print('## link state file')
6421 output = read_link_state_file('veth99')
6422 print(output)
b5c8f471 6423 # checking DNS server, SIP server, and Domains
1a8e1d78 6424 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
b5c8f471 6425 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
1a8e1d78 6426 self.assertIn('DOMAINS=foo.example.com', output)
18f2638f 6427
b5c8f471 6428 print('## json')
10d670a3 6429 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
6430
6431 self.assertEqual(len(j['DNS']), 3)
6432 for i in j['DNS']:
6433 print(i)
6434 self.assertEqual(i['Family'], 2)
6435 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6436 self.assertRegex(a, '^192.168.5.[178]$')
6437 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6438 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6439 self.assertEqual('192.168.5.1', a)
6440
6441 self.assertEqual(len(j['SIP']), 2)
6442 for i in j['SIP']:
6443 print(i)
6444 self.assertEqual(i['Family'], 2)
6445 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6446 self.assertRegex(a, '^192.168.5.2[34]$')
6447 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6448 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6449 self.assertEqual('192.168.5.1', a)
6450
18f2638f 6451 print('## dnsmasq log')
063c7e9b
YW
6452 output = read_dnsmasq_log_file()
6453 print(output)
6454 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 6455 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
063c7e9b
YW
6456 self.assertIn('client provides name: test-hostname', output)
6457 self.assertIn('26:mtu', output)
1f0e3109 6458
0677130e 6459 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
a4640bed 6460
c742d7e8
TM
6461 self.check_nftset('addr4', r'192\.168\.5\.1')
6462 self.check_nftset('network4', r'192\.168\.5\.0/24')
6463 self.check_nftset('ifindex', 'veth99')
6464
6465 self.teardown_nftset('addr4', 'network4', 'ifindex')
6466
e1ef7771 6467 def test_dhcp_client_ipv4_dbus_status(self):
e1ef7771 6468 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
e1ef7771 6469 start_networkd()
95e1fbba 6470 self.wait_online('veth-peer:carrier')
e1ef7771 6471
e081ffc1 6472 state = get_dhcp4_client_state('veth99')
e1ef7771 6473 print(f"State = {state}")
6b524d70 6474 self.assertEqual(state, 'rebooting')
e1ef7771 6475
6476 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6477 '--dhcp-option=option:domain-search,example.com',
6478 '--dhcp-alternate-port=67,5555',
6479 ipv4_range='192.168.5.110,192.168.5.119')
95e1fbba 6480 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 6481 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6482
e081ffc1 6483 state = get_dhcp4_client_state('veth99')
e1ef7771 6484 print(f"State = {state}")
6485 self.assertEqual(state, 'bound')
6486
b65c5390
YW
6487 def test_dhcp_client_allow_list(self):
6488 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
6489
6490 start_networkd()
95e1fbba 6491 self.wait_online('veth-peer:carrier')
b65c5390
YW
6492 since = datetime.datetime.now()
6493 start_dnsmasq()
6494
6495 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6496 for _ in range(20):
6497 if expect in read_networkd_log(since=since):
6498 break
6499 time.sleep(0.5)
6500 else:
6501 self.fail()
6502
6503 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6504 since = datetime.datetime.now()
6505 networkctl_reload()
6506
6507 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6508 for _ in range(20):
6509 if expect in read_networkd_log(since=since):
6510 break
6511 time.sleep(0.5)
6512 else:
6513 self.fail()
6514
6515 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6516 since = datetime.datetime.now()
6517 networkctl_reload()
6518
6519 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6520 for _ in range(20):
6521 if expect in read_networkd_log(since=since):
6522 break
6523 time.sleep(0.5)
6524 else:
6525 self.fail()
6526
2beecc70
RP
6527 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
6528 def test_dhcp_client_rapid_commit(self):
6529 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6530 start_networkd()
95e1fbba 6531 self.wait_online('veth-peer:carrier')
2beecc70
RP
6532
6533 start_dnsmasq('--dhcp-rapid-commit')
95e1fbba 6534 self.wait_online('veth99:routable', 'veth-peer:routable')
2beecc70
RP
6535 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6536
6537 state = get_dhcp4_client_state('veth99')
6538 print(f"DHCPv4 client state = {state}")
6539 self.assertEqual(state, 'bound')
6540
6541 output = read_dnsmasq_log_file()
6542 self.assertIn('DHCPDISCOVER(veth-peer)', output)
6543 self.assertNotIn('DHCPOFFER(veth-peer)', output)
6544 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6545 self.assertIn('DHCPACK(veth-peer)', output)
6546
46f2eb51
YW
6547 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
6548 copy_network_unit('25-veth.netdev',
6549 '25-dhcp-server-ipv6-only-mode.network',
6550 '25-dhcp-client-ipv6-only-mode.network')
6551 start_networkd()
95e1fbba 6552 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
46f2eb51
YW
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"State = {state}")
6557 self.assertEqual(state, 'bound')
6558
7c0d36ff 6559 def test_dhcp_client_ipv4_use_routes_gateway(self):
a962d857 6560 first = True
e1220a70 6561 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
a962d857
YW
6562 if first:
6563 first = False
6564 else:
6565 self.tearDown()
6566
6567 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
6568 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
6569 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
7c0d36ff 6570
e1220a70 6571 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
1e86c833
DDM
6572 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6573 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
6983bb0e
FS
6574 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
6575 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
6576 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
a962d857 6577 copy_network_unit(*testunits, copy_dropins=False)
4c2e1833
YW
6578
6579 start_networkd()
95e1fbba 6580 self.wait_online('veth-peer:carrier')
a962d857
YW
6581 additional_options = [
6582 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6583 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
4001653d 6584 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
a962d857 6585 ]
625772c9 6586 if classless:
a962d857 6587 additional_options += [
86f67600 6588 '--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
6589 ]
6590 start_dnsmasq(*additional_options)
95e1fbba 6591 self.wait_online('veth99:routable', 'veth-peer:routable')
4c2e1833 6592
625772c9 6593 output = check_output('ip -4 route show dev veth99')
4c2e1833 6594 print(output)
4c2e1833 6595
7c0d36ff 6596 # Check UseRoutes=
625772c9
YW
6597 if use_routes:
6598 if classless:
6599 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6600 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 6601 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
6602 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6603 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6604 else:
4001653d 6605 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 6606 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 6607 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6608 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6609 else:
625772c9
YW
6610 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6611 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6612 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6613 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4001653d 6614 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 6615 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 6616 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6617 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0d7bd445 6618
7c0d36ff 6619 # Check UseGateway=
625772c9
YW
6620 if use_gateway and (not classless or not use_routes):
6621 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6622 else:
625772c9 6623 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32
YW
6624
6625 # Check route to gateway
6626 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
6627 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6628 else:
625772c9 6629 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6630
e1220a70
YW
6631 # Check RoutesToDNS= and RoutesToNTP=
6632 if dns_and_ntp_routes:
625772c9 6633 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 6634 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0ce86f5e
YW
6635 if use_routes:
6636 if classless:
6637 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6638 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6639 else:
6640 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6641 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32 6642 else:
625772c9 6643 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
e1220a70 6644 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 6645 else:
625772c9 6646 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 6647 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 6648 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 6649 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 6650
10d670a3 6651 check_json(networkctl_json())
94f0bd62 6652
1f0e3109 6653 def test_dhcp_client_settings_anonymize(self):
a962d857 6654 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
2cf6fdff 6655 start_networkd()
95e1fbba 6656 self.wait_online('veth-peer:carrier')
ec38833c 6657 start_dnsmasq()
95e1fbba 6658 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 6659
063c7e9b
YW
6660 print('## dnsmasq log')
6661 output = read_dnsmasq_log_file()
6662 print(output)
6663 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
6664 self.assertNotIn('test-hostname', output)
6665 self.assertNotIn('26:mtu', output)
1f0e3109 6666
1e498853 6667 def test_dhcp_keep_configuration_dhcp(self):
a962d857
YW
6668 copy_network_unit('25-veth.netdev',
6669 '25-dhcp-server-veth-peer.network',
6670 '25-dhcp-client-keep-configuration-dhcp.network')
2cf6fdff 6671 start_networkd()
95e1fbba 6672 self.wait_online('veth-peer:carrier')
a962d857 6673 start_dnsmasq()
95e1fbba 6674 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 6675
1e498853
YW
6676 output = check_output('ip address show dev veth99 scope global')
6677 print(output)
2b6a24a6
YW
6678 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6679 'valid_lft forever preferred_lft forever')
e40a58b5 6680
5238e957 6681 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
888f57c1 6682 stop_dnsmasq()
1f0e3109
SS
6683
6684 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c
YW
6685 print('Wait for the DHCP lease to be expired')
6686 time.sleep(120)
1f0e3109 6687
2b6a24a6 6688 # The lease address should be kept after the lease expired
1e498853
YW
6689 output = check_output('ip address show dev veth99 scope global')
6690 print(output)
2b6a24a6
YW
6691 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6692 'valid_lft forever preferred_lft forever')
1e498853 6693
2b6a24a6 6694 stop_networkd()
1e498853 6695
2b6a24a6 6696 # The lease address should be kept after networkd stopped
1e498853
YW
6697 output = check_output('ip address show dev veth99 scope global')
6698 print(output)
2b6a24a6
YW
6699 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6700 'valid_lft forever preferred_lft forever')
1e498853 6701
a962d857 6702 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
2347b6b9 6703 f.write('[Network]\nDHCP=no\n')
1e498853 6704
2347b6b9 6705 start_networkd()
95e1fbba 6706 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853 6707
2b6a24a6 6708 # Still the lease address should be kept after networkd restarted
1e498853
YW
6709 output = check_output('ip address show dev veth99 scope global')
6710 print(output)
2b6a24a6
YW
6711 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6712 'valid_lft forever preferred_lft forever')
1e498853
YW
6713
6714 def test_dhcp_keep_configuration_dhcp_on_stop(self):
a962d857
YW
6715 copy_network_unit('25-veth.netdev',
6716 '25-dhcp-server-veth-peer.network',
6717 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
2cf6fdff 6718 start_networkd()
95e1fbba 6719 self.wait_online('veth-peer:carrier')
a962d857 6720 start_dnsmasq()
95e1fbba 6721 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853
YW
6722
6723 output = check_output('ip address show dev veth99 scope global')
6724 print(output)
2b6a24a6 6725 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 6726
888f57c1 6727 stop_dnsmasq()
2b6a24a6 6728 stop_networkd()
1e498853
YW
6729
6730 output = check_output('ip address show dev veth99 scope global')
6731 print(output)
2b6a24a6 6732 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 6733
a962d857 6734 start_networkd()
95e1fbba 6735 self.wait_online('veth-peer:routable')
1e498853
YW
6736
6737 output = check_output('ip address show dev veth99 scope global')
6738 print(output)
2b6a24a6 6739 self.assertNotIn('192.168.5.', output)
1f0e3109 6740
30d3b54e 6741 def test_dhcp_client_reuse_address_as_static(self):
a962d857 6742 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
2cf6fdff 6743 start_networkd()
95e1fbba 6744 self.wait_online('veth-peer:carrier')
ec38833c 6745 start_dnsmasq()
95e1fbba 6746 self.wait_online('veth99:routable', 'veth-peer:routable')
2629df47
YW
6747
6748 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 6749 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 6750 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
30d3b54e 6751
371810d1 6752 output = check_output('ip address show dev veth99 scope global')
15519a81
YW
6753 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
6754 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
6755 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
30d3b54e
YW
6756 print(static_network)
6757
a962d857 6758 remove_network_unit('25-dhcp-client.network')
30d3b54e 6759
a962d857 6760 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
30d3b54e
YW
6761 f.write(static_network)
6762
15519a81 6763 restart_networkd()
95e1fbba 6764 self.wait_online('veth99:routable')
30d3b54e 6765
371810d1 6766 output = check_output('ip -4 address show dev veth99 scope global')
30d3b54e 6767 print(output)
15519a81
YW
6768 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6769 'valid_lft forever preferred_lft forever')
30d3b54e 6770
371810d1 6771 output = check_output('ip -6 address show dev veth99 scope global')
30d3b54e 6772 print(output)
15519a81
YW
6773 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
6774 'valid_lft forever preferred_lft forever')
30d3b54e 6775
18c613dc
YW
6776 @expectedFailureIfModuleIsNotAvailable('vrf')
6777 def test_dhcp_client_vrf(self):
a962d857
YW
6778 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6779 '25-vrf.netdev', '25-vrf.network')
2cf6fdff 6780 start_networkd()
95e1fbba 6781 self.wait_online('veth-peer:carrier')
ec38833c 6782 start_dnsmasq()
95e1fbba 6783 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
2629df47
YW
6784
6785 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 6786 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 6787 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
18c613dc
YW
6788
6789 print('## ip -d link show dev vrf99')
371810d1 6790 output = check_output('ip -d link show dev vrf99')
18c613dc
YW
6791 print(output)
6792 self.assertRegex(output, 'vrf table 42')
6793
6794 print('## ip address show vrf vrf99')
371810d1 6795 output = check_output('ip address show vrf vrf99')
d90f4f7d 6796 print(output)
3e726c15 6797 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 6798 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
d90f4f7d 6799 self.assertRegex(output, 'inet6 .* scope link')
18c613dc
YW
6800
6801 print('## ip address show dev veth99')
371810d1 6802 output = check_output('ip address show dev veth99')
18c613dc 6803 print(output)
3e726c15 6804 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 6805 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
18c613dc
YW
6806 self.assertRegex(output, 'inet6 .* scope link')
6807
6808 print('## ip route show vrf vrf99')
371810d1 6809 output = check_output('ip route show vrf vrf99')
18c613dc
YW
6810 print(output)
6811 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
18c613dc 6812 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
18c613dc
YW
6813 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6814
6815 print('## ip route show table main dev veth99')
371810d1 6816 output = check_output('ip route show table main dev veth99')
18c613dc
YW
6817 print(output)
6818 self.assertEqual(output, '')
6819
af3b1498 6820 def test_dhcp_client_gateway_onlink_implicit(self):
a962d857
YW
6821 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6822 '25-dhcp-client-gateway-onlink-implicit.network')
2cf6fdff 6823 start_networkd()
95e1fbba 6824 self.wait_online('veth-peer:carrier')
ec38833c 6825 start_dnsmasq()
95e1fbba 6826 self.wait_online('veth99:routable', 'veth-peer:routable')
af3b1498 6827
10d670a3 6828 output = networkctl_status('veth99')
af3b1498
YW
6829 print(output)
6830 self.assertRegex(output, '192.168.5')
6831
371810d1 6832 output = check_output('ip route list dev veth99 10.0.0.0/8')
af3b1498
YW
6833 print(output)
6834 self.assertRegex(output, 'onlink')
371810d1 6835 output = check_output('ip route list dev veth99 192.168.100.0/24')
af3b1498
YW
6836 print(output)
6837 self.assertRegex(output, 'onlink')
6838
2d7a594f 6839 def test_dhcp_client_with_ipv4ll(self):
a962d857
YW
6840 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6841 '25-dhcp-client-with-ipv4ll.network')
2cf6fdff 6842 start_networkd()
2d7a594f
YW
6843 # we need to increase timeout above default, as this will need to wait for
6844 # systemd-networkd to get the dhcpv4 transient failure event
95e1fbba 6845 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
63c598ed 6846
2d7a594f 6847 output = check_output('ip -4 address show dev veth99')
63c598ed 6848 print(output)
2d7a594f 6849 self.assertNotIn('192.168.5.', output)
72c747e6 6850 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
63c598ed 6851
a962d857 6852 start_dnsmasq()
2d7a594f
YW
6853 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6854 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
6855 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 6856 self.wait_online('veth99:routable')
63c598ed 6857
2d7a594f 6858 output = check_output('ip -4 address show dev veth99')
63c598ed 6859 print(output)
3e726c15 6860 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
2d7a594f
YW
6861 self.assertNotIn('169.254.', output)
6862 self.assertNotIn('scope link', output)
63c598ed 6863
2d7a594f
YW
6864 stop_dnsmasq()
6865 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
286bf3a9 6866 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 6867 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 6868
2d7a594f 6869 output = check_output('ip -4 address show dev veth99')
117a55c7 6870 print(output)
2d7a594f 6871 self.assertNotIn('192.168.5.', output)
72c747e6 6872 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
240e4137 6873
3d8e0aa2
YW
6874 def test_dhcp_client_use_dns(self):
6875 def check(self, ipv4, ipv6):
a962d857
YW
6876 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6877 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
3d8e0aa2
YW
6878 f.write('[DHCPv4]\nUseDNS=')
6879 f.write('yes' if ipv4 else 'no')
6880 f.write('\n[DHCPv6]\nUseDNS=')
6881 f.write('yes' if ipv6 else 'no')
6882 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
6883
a962d857 6884 networkctl_reload()
95e1fbba 6885 self.wait_online('veth99:routable')
e2d5aab3 6886
3d8e0aa2
YW
6887 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6888 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6889 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
e2d5aab3 6890
3d8e0aa2 6891 # make resolved re-read the link state file
10d670a3 6892 resolvectl('revert', 'veth99')
e2d5aab3 6893
10d670a3 6894 output = resolvectl('dns', 'veth99')
3d8e0aa2
YW
6895 print(output)
6896 if ipv4:
6897 self.assertIn('192.168.5.1', output)
6898 else:
6899 self.assertNotIn('192.168.5.1', output)
6900 if ipv6:
6901 self.assertIn('2600::1', output)
6902 else:
6903 self.assertNotIn('2600::1', output)
e2d5aab3 6904
10d670a3 6905 check_json(networkctl_json())
e2d5aab3 6906
a962d857 6907 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
e2d5aab3
YW
6908
6909 start_networkd()
95e1fbba 6910 self.wait_online('veth-peer:carrier')
a962d857
YW
6911 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6912 '--dhcp-option=option6:dns-server,[2600::1]')
e2d5aab3 6913
3d8e0aa2
YW
6914 check(self, True, True)
6915 check(self, True, False)
6916 check(self, False, True)
6917 check(self, False, False)
e2d5aab3 6918
dbe960f0
RP
6919 def test_dhcp_client_use_captive_portal(self):
6920 def check(self, ipv4, ipv6):
6921 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6922 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6923 f.write('[DHCPv4]\nUseCaptivePortal=')
6924 f.write('yes' if ipv4 else 'no')
6925 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6926 f.write('yes' if ipv6 else 'no')
6927 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6928
6929 networkctl_reload()
95e1fbba 6930 self.wait_online('veth99:routable')
dbe960f0
RP
6931
6932 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6933 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6934 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6935
10d670a3 6936 output = networkctl_status('veth99')
dbe960f0
RP
6937 print(output)
6938 if ipv4 or ipv6:
6939 self.assertIn('Captive Portal: http://systemd.io', output)
6940 else:
6941 self.assertNotIn('Captive Portal: http://systemd.io', output)
6942
10d670a3 6943 check_json(networkctl_json())
dbe960f0
RP
6944
6945 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6946
6947 start_networkd()
95e1fbba 6948 self.wait_online('veth-peer:carrier')
dbe960f0
RP
6949 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6950 '--dhcp-option=option6:103,http://systemd.io')
6951
6952 check(self, True, True)
6953 check(self, True, False)
6954 check(self, False, True)
6955 check(self, False, False)
6956
1219391c
RP
6957 def test_dhcp_client_reject_captive_portal(self):
6958 def check(self, ipv4, ipv6):
6959 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6960 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6961 f.write('[DHCPv4]\nUseCaptivePortal=')
6962 f.write('yes' if ipv4 else 'no')
6963 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6964 f.write('yes' if ipv6 else 'no')
6965 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6966
6967 networkctl_reload()
95e1fbba 6968 self.wait_online('veth99:routable')
1219391c
RP
6969
6970 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6971 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6972 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6973
10d670a3 6974 output = networkctl_status('veth99')
1219391c
RP
6975 print(output)
6976 self.assertNotIn('Captive Portal: ', output)
6977 self.assertNotIn('invalid/url', output)
6978
10d670a3 6979 check_json(networkctl_json())
1219391c
RP
6980
6981 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6982
6983 start_networkd()
95e1fbba 6984 self.wait_online('veth-peer:carrier')
1219391c
RP
6985 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
6986 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
6987 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
6988
6989 check(self, True, True)
6990 check(self, True, False)
6991 check(self, False, True)
6992 check(self, False, False)
6993
a27588d4 6994class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
caad88a2
YW
6995
6996 def setUp(self):
a962d857 6997 setup_common()
caad88a2
YW
6998
6999 def tearDown(self):
a962d857 7000 tear_down_common()
caad88a2 7001
ab06b74f
YW
7002 def check_dhcp6_prefix(self, link):
7003 description = get_link_description(link)
8fb6320e 7004
ab06b74f
YW
7005 self.assertIn('DHCPv6Client', description.keys())
7006 self.assertIn('Prefixes', description['DHCPv6Client'])
8fb6320e 7007
ab06b74f 7008 prefixInfo = description['DHCPv6Client']['Prefixes']
8fb6320e 7009
ab06b74f 7010 self.assertEqual(len(prefixInfo), 1)
8fb6320e 7011
ab06b74f
YW
7012 self.assertIn('Prefix', prefixInfo[0].keys())
7013 self.assertIn('PrefixLength', prefixInfo[0].keys())
7014 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
7015 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
caad88a2 7016
ab06b74f
YW
7017 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
7018 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
7019 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
7020 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
7021
7022 def test_dhcp6pd_no_address(self):
7023 # For issue #29979.
7024 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
c742d7e8 7025
caad88a2 7026 start_networkd()
95e1fbba 7027 self.wait_online('veth-peer:routable')
a962d857 7028 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba 7029 self.wait_online('veth99:degraded')
1805e2cb 7030
1805e2cb
YW
7031 print('### ip -6 address show dev veth99 scope global')
7032 output = check_output('ip -6 address show dev veth99 scope global')
7033 print(output)
7034 self.assertNotIn('inet6 3ffe:501:ffff', output)
caad88a2 7035
ab06b74f 7036 self.check_dhcp6_prefix('veth99')
8fb6320e 7037
3b677c6f
YW
7038 def test_dhcp6pd_no_assign(self):
7039 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
7040 # However, the server does not provide IA_NA. For issue #31349.
7041 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
7042
7043 start_networkd()
7044 self.wait_online('veth-peer:routable')
7045 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
7046 self.wait_online('veth99:degraded')
7047
7048 print('### ip -6 address show dev veth99 scope global')
7049 output = check_output('ip -6 address show dev veth99 scope global')
7050 print(output)
7051 self.assertNotIn('inet6 3ffe:501:ffff', output)
7052
7053 self.check_dhcp6_prefix('veth99')
7054
ab06b74f
YW
7055 def test_dhcp6pd(self):
7056 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
7057 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7058 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7059 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7060 '25-dhcp-pd-downstream-dummy97.network',
7061 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7062 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
8fb6320e 7063
ab06b74f
YW
7064 start_networkd()
7065 self.wait_online('veth-peer:routable')
7066 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba
YW
7067 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7068 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
1805e2cb 7069
ab06b74f
YW
7070 self.setup_nftset('addr6', 'ipv6_addr')
7071 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
7072 self.setup_nftset('ifindex', 'iface_index')
7073
7074 # Check DBus assigned prefix information to veth99
7075 self.check_dhcp6_prefix('veth99')
7076
caad88a2
YW
7077 print('### ip -6 address show dev veth-peer scope global')
7078 output = check_output('ip -6 address show dev veth-peer scope global')
7079 print(output)
7080 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
7081
f7805a6c
FS
7082 # Link Subnet IDs
7083 # test1: 0x00
7084 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
7085 # dummy98: 0x00
7086 # dummy99: auto -> 0x02 (No address assignment)
f7805a6c
FS
7087 # veth97: 0x08
7088 # veth98: 0x09
38488bab 7089 # veth99: 0x10
6016f1cf 7090
caad88a2
YW
7091 print('### ip -6 address show dev veth99 scope global')
7092 output = check_output('ip -6 address show dev veth99 scope global')
7093 print(output)
7094 # IA_NA
7095 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
7096 # address in IA_PD (Token=static)
38488bab 7097 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
caad88a2 7098 # address in IA_PD (Token=eui64)
38488bab
YW
7099 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
7100 # address in IA_PD (temporary)
7101 # Note that the temporary addresses may appear after the link enters configured state
7102 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
7103
7104 print('### ip -6 address show dev test1 scope global')
7105 output = check_output('ip -6 address show dev test1 scope global')
7106 print(output)
7107 # address in IA_PD (Token=static)
7108 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7109 # address in IA_PD (temporary)
7110 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')
7111
7112 print('### ip -6 address show dev dummy98 scope global')
7113 output = check_output('ip -6 address show dev dummy98 scope global')
7114 print(output)
7115 # address in IA_PD (Token=static)
07b7337a 7116 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7117 # address in IA_PD (temporary)
07b7337a 7118 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
7119
7120 print('### ip -6 address show dev dummy99 scope global')
7121 output = check_output('ip -6 address show dev dummy99 scope global')
7122 print(output)
7123 # Assign=no
07b7337a 7124 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2 7125
6c8d6bdd
YW
7126 print('### ip -6 address show dev veth97 scope global')
7127 output = check_output('ip -6 address show dev veth97 scope global')
7128 print(output)
7129 # address in IA_PD (Token=static)
7130 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7131 # address in IA_PD (Token=eui64)
7132 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7133 # address in IA_PD (temporary)
7134 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')
7135
7136 print('### ip -6 address show dev veth97-peer scope global')
7137 output = check_output('ip -6 address show dev veth97-peer scope global')
7138 print(output)
7139 # NDisc address (Token=static)
7140 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7141 # NDisc address (Token=eui64)
7142 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7143 # NDisc address (temporary)
7144 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')
7145
caad88a2
YW
7146 print('### ip -6 address show dev veth98 scope global')
7147 output = check_output('ip -6 address show dev veth98 scope global')
7148 print(output)
7149 # address in IA_PD (Token=static)
7150 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7151 # address in IA_PD (Token=eui64)
7152 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7153 # address in IA_PD (temporary)
7154 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')
7155
7156 print('### ip -6 address show dev veth98-peer scope global')
7157 output = check_output('ip -6 address show dev veth98-peer scope global')
7158 print(output)
7159 # NDisc address (Token=static)
7160 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7161 # NDisc address (Token=eui64)
7162 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7163 # NDisc address (temporary)
7164 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')
7165
caad88a2
YW
7166 print('### ip -6 route show type unreachable')
7167 output = check_output('ip -6 route show type unreachable')
7168 print(output)
7169 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
7170
7171 print('### ip -6 route show dev veth99')
7172 output = check_output('ip -6 route show dev veth99')
7173 print(output)
38488bab 7174 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7175
7176 print('### ip -6 route show dev test1')
7177 output = check_output('ip -6 route show dev test1')
7178 print(output)
7179 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7180
7181 print('### ip -6 route show dev dummy98')
7182 output = check_output('ip -6 route show dev dummy98')
7183 print(output)
07b7337a 7184 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7185
7186 print('### ip -6 route show dev dummy99')
7187 output = check_output('ip -6 route show dev dummy99')
7188 print(output)
07b7337a 7189 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6016f1cf
YW
7190
7191 print('### ip -6 route show dev veth97')
7192 output = check_output('ip -6 route show dev veth97')
7193 print(output)
7194 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
7195
7196 print('### ip -6 route show dev veth97-peer')
7197 output = check_output('ip -6 route show dev veth97-peer')
7198 print(output)
7199 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
caad88a2
YW
7200
7201 print('### ip -6 route show dev veth98')
7202 output = check_output('ip -6 route show dev veth98')
7203 print(output)
7204 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
7205
7206 print('### ip -6 route show dev veth98-peer')
7207 output = check_output('ip -6 route show dev veth98-peer')
7208 print(output)
7209 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
7210
7211 # Test case for a downstream which appears later
7212 check_output('ip link add dummy97 type dummy')
95e1fbba 7213 self.wait_online('dummy97:routable')
caad88a2
YW
7214
7215 print('### ip -6 address show dev dummy97 scope global')
7216 output = check_output('ip -6 address show dev dummy97 scope global')
7217 print(output)
7218 # address in IA_PD (Token=static)
6016f1cf 7219 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7220 # address in IA_PD (temporary)
6016f1cf 7221 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
7222
7223 print('### ip -6 route show dev dummy97')
7224 output = check_output('ip -6 route show dev dummy97')
7225 print(output)
6016f1cf 7226 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
7227
7228 # Test case for reconfigure
a962d857 7229 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 7230 self.wait_online('dummy98:routable', 'dummy99:degraded')
caad88a2
YW
7231
7232 print('### ip -6 address show dev dummy98 scope global')
7233 output = check_output('ip -6 address show dev dummy98 scope global')
7234 print(output)
7235 # address in IA_PD (Token=static)
07b7337a 7236 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 7237 # address in IA_PD (temporary)
07b7337a 7238 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
7239
7240 print('### ip -6 address show dev dummy99 scope global')
7241 output = check_output('ip -6 address show dev dummy99 scope global')
7242 print(output)
7243 # Assign=no
07b7337a 7244 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2
YW
7245
7246 print('### ip -6 route show dev dummy98')
7247 output = check_output('ip -6 route show dev dummy98')
7248 print(output)
07b7337a 7249 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6016f1cf
YW
7250
7251 print('### ip -6 route show dev dummy99')
7252 output = check_output('ip -6 route show dev dummy99')
7253 print(output)
07b7337a 7254 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
caad88a2 7255
a4640bed
TM
7256 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
7257
c742d7e8
TM
7258 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
7259 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
7260 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
7261 self.check_nftset('ifindex', 'dummy98')
7262
7263 self.teardown_nftset('addr6', 'network6', 'ifindex')
7264
6a936c9c 7265 def verify_dhcp4_6rd(self, tunnel_name):
84cc85f9
YW
7266 print('### ip -4 address show dev veth-peer scope global')
7267 output = check_output('ip -4 address show dev veth-peer scope global')
7268 print(output)
7269 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
7270
f7805a6c
FS
7271 # Link Subnet IDs
7272 # test1: 0x00
7273 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
7274 # dummy98: 0x00
7275 # dummy99: auto -> 0x0[23] (No address assignment)
7276 # 6rd-XXX: auto -> 0x0[23]
f7805a6c
FS
7277 # veth97: 0x08
7278 # veth98: 0x09
7279 # veth99: 0x10
84cc85f9
YW
7280
7281 print('### ip -4 address show dev veth99 scope global')
7282 output = check_output('ip -4 address show dev veth99 scope global')
7283 print(output)
7284 self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7285
7286 print('### ip -6 address show dev veth99 scope global')
7287 output = check_output('ip -6 address show dev veth99 scope global')
7288 print(output)
7289 # address in IA_PD (Token=static)
7290 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7291 # address in IA_PD (Token=eui64)
7292 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7293 # address in IA_PD (temporary)
7294 # Note that the temporary addresses may appear after the link enters configured state
7295 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')
7296
7297 print('### ip -6 address show dev test1 scope global')
7298 output = check_output('ip -6 address show dev test1 scope global')
7299 print(output)
7300 # address in IA_PD (Token=static)
7301 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7302 # address in IA_PD (temporary)
7303 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')
7304
7305 print('### ip -6 address show dev dummy98 scope global')
7306 output = check_output('ip -6 address show dev dummy98 scope global')
7307 print(output)
7308 # address in IA_PD (Token=static)
07b7337a 7309 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
84cc85f9 7310 # address in IA_PD (temporary)
07b7337a 7311 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
7312
7313 print('### ip -6 address show dev dummy99 scope global')
7314 output = check_output('ip -6 address show dev dummy99 scope global')
7315 print(output)
7316 # Assign=no
07b7337a 7317 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
84cc85f9
YW
7318
7319 print('### ip -6 address show dev veth97 scope global')
7320 output = check_output('ip -6 address show dev veth97 scope global')
7321 print(output)
7322 # address in IA_PD (Token=static)
7323 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7324 # address in IA_PD (Token=eui64)
7325 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7326 # address in IA_PD (temporary)
7327 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')
7328
7329 print('### ip -6 address show dev veth97-peer scope global')
7330 output = check_output('ip -6 address show dev veth97-peer scope global')
7331 print(output)
7332 # NDisc address (Token=static)
7333 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7334 # NDisc address (Token=eui64)
7335 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7336 # NDisc address (temporary)
7337 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')
7338
7339 print('### ip -6 address show dev veth98 scope global')
7340 output = check_output('ip -6 address show dev veth98 scope global')
7341 print(output)
7342 # address in IA_PD (Token=static)
7343 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7344 # address in IA_PD (Token=eui64)
7345 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7346 # address in IA_PD (temporary)
7347 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')
7348
7349 print('### ip -6 address show dev veth98-peer scope global')
7350 output = check_output('ip -6 address show dev veth98-peer scope global')
7351 print(output)
7352 # NDisc address (Token=static)
7353 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7354 # NDisc address (Token=eui64)
7355 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7356 # NDisc address (temporary)
7357 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')
7358
7359 print('### ip -6 route show type unreachable')
7360 output = check_output('ip -6 route show type unreachable')
7361 print(output)
7362 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7363
7364 print('### ip -6 route show dev veth99')
7365 output = check_output('ip -6 route show dev veth99')
7366 print(output)
7367 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7368
7369 print('### ip -6 route show dev test1')
7370 output = check_output('ip -6 route show dev test1')
7371 print(output)
7372 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7373
7374 print('### ip -6 route show dev dummy98')
7375 output = check_output('ip -6 route show dev dummy98')
7376 print(output)
07b7337a 7377 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
7378
7379 print('### ip -6 route show dev dummy99')
7380 output = check_output('ip -6 route show dev dummy99')
7381 print(output)
07b7337a 7382 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
84cc85f9
YW
7383
7384 print('### ip -6 route show dev veth97')
7385 output = check_output('ip -6 route show dev veth97')
7386 print(output)
7387 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7388
7389 print('### ip -6 route show dev veth97-peer')
7390 output = check_output('ip -6 route show dev veth97-peer')
7391 print(output)
7392 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7393
7394 print('### ip -6 route show dev veth98')
7395 output = check_output('ip -6 route show dev veth98')
7396 print(output)
7397 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7398
7399 print('### ip -6 route show dev veth98-peer')
7400 output = check_output('ip -6 route show dev veth98-peer')
7401 print(output)
7402 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7403
84cc85f9
YW
7404 print('### ip -6 address show dev dummy97 scope global')
7405 output = check_output('ip -6 address show dev dummy97 scope global')
7406 print(output)
7407 # address in IA_PD (Token=static)
7408 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7409 # address in IA_PD (temporary)
7410 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')
7411
7412 print('### ip -6 route show dev dummy97')
7413 output = check_output('ip -6 route show dev dummy97')
7414 print(output)
7415 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7416
e4295d4d
FS
7417 print(f'### ip -d link show dev {tunnel_name}')
7418 output = check_output(f'ip -d link show dev {tunnel_name}')
84cc85f9
YW
7419 print(output)
7420 self.assertIn('link/sit 10.100.100.', output)
7421 self.assertIn('local 10.100.100.', output)
84cc85f9
YW
7422 self.assertIn('ttl 64', output)
7423 self.assertIn('6rd-prefix 2001:db8::/32', output)
7424 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
7425
e4295d4d
FS
7426 print(f'### ip -6 address show dev {tunnel_name}')
7427 output = check_output(f'ip -6 address show dev {tunnel_name}')
84cc85f9 7428 print(output)
07b7337a 7429 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
7430 self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7431
e4295d4d
FS
7432 print(f'### ip -6 route show dev {tunnel_name}')
7433 output = check_output(f'ip -6 route show dev {tunnel_name}')
84cc85f9 7434 print(output)
07b7337a 7435 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
7436 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
7437
7438 print('### ip -6 route show default')
7439 output = check_output('ip -6 route show default')
7440 print(output)
7441 self.assertIn('default', output)
e4295d4d 7442 self.assertIn(f'via ::10.0.0.1 dev {tunnel_name}', output)
84cc85f9 7443
6a936c9c 7444 def test_dhcp4_6rd(self):
e081ffc1
YW
7445 def get_dhcp_6rd_prefix(link):
7446 description = get_link_description(link)
8fb6320e 7447
7448 self.assertIn('DHCPv4Client', description.keys())
7449 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
7450
7451 prefixInfo = description['DHCPv4Client']['6rdPrefix']
7452 self.assertIn('Prefix', prefixInfo.keys())
7453 self.assertIn('PrefixLength', prefixInfo.keys())
7454 self.assertIn('IPv4MaskLength', prefixInfo.keys())
7455 self.assertIn('BorderRouters', prefixInfo.keys())
7456
7457 return prefixInfo
7458
a962d857
YW
7459 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7460 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7461 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7462 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7463 '25-dhcp-pd-downstream-dummy97.network',
7464 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7465 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7466 '80-6rd-tunnel.network')
6a936c9c
YW
7467
7468 start_networkd()
95e1fbba 7469 self.wait_online('veth-peer:routable')
f7805a6c
FS
7470
7471 # ipv4masklen: 8
7472 # 6rd-prefix: 2001:db8::/32
7473 # br-addresss: 10.0.0.1
7474
a962d857
YW
7475 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',
7476 ipv4_range='10.100.100.100,10.100.100.200',
7477 ipv4_router='10.0.0.1')
95e1fbba
YW
7478 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7479 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c 7480
8fb6320e 7481 # Check the DBus interface for assigned prefix information
e081ffc1 7482 prefixInfo = get_dhcp_6rd_prefix('veth99')
8fb6320e 7483
7484 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7485 self.assertEqual(prefixInfo['PrefixLength'], 32)
7486 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
7487 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
7488
6a936c9c
YW
7489 # Test case for a downstream which appears later
7490 check_output('ip link add dummy97 type dummy')
95e1fbba 7491 self.wait_online('dummy97:routable')
6a936c9c
YW
7492
7493 # Find tunnel name
7494 tunnel_name = None
7495 for name in os.listdir('/sys/class/net/'):
7496 if name.startswith('6rd-'):
7497 tunnel_name = name
7498 break
7499
95e1fbba 7500 self.wait_online(f'{tunnel_name}:routable')
6a936c9c
YW
7501
7502 self.verify_dhcp4_6rd(tunnel_name)
7503
7504 # Test case for reconfigure
a962d857 7505 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 7506 self.wait_online('dummy98:routable', 'dummy99:degraded')
6a936c9c
YW
7507
7508 self.verify_dhcp4_6rd(tunnel_name)
7509
3af934bc 7510 print('Wait for the DHCP lease to be renewed/rebind')
a102a52c 7511 time.sleep(120)
6a936c9c 7512
95e1fbba
YW
7513 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7514 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c
YW
7515
7516 self.verify_dhcp4_6rd(tunnel_name)
7517
9633f977 7518class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
9633f977
SS
7519
7520 def setUp(self):
a962d857 7521 setup_common()
9633f977
SS
7522
7523 def tearDown(self):
a962d857 7524 tear_down_common()
9633f977
SS
7525
7526 def test_ipv6_route_prefix(self):
a962d857
YW
7527 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7528 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9633f977
SS
7529
7530 start_networkd()
95e1fbba 7531 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9633f977 7532
635f2a66
YW
7533 output = check_output('ip address show dev veth-peer')
7534 print(output)
7535 self.assertIn('inet6 2001:db8:0:1:', output)
7536 self.assertNotIn('inet6 2001:db8:0:2:', output)
ab47f960 7537 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 7538
3c874fd7 7539 output = check_output('ip -6 route show dev veth-peer')
9633f977 7540 print(output)
635f2a66
YW
7541 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7542 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
ab47f960 7543 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
635f2a66
YW
7544 self.assertIn('2001:db0:fff::/64 via ', output)
7545 self.assertNotIn('2001:db1:fff::/64 via ', output)
ab47f960 7546 self.assertNotIn('2001:db2:fff::/64 via ', output)
9633f977 7547
635f2a66
YW
7548 output = check_output('ip address show dev veth99')
7549 print(output)
7550 self.assertNotIn('inet6 2001:db8:0:1:', output)
fe2a8b3d
YW
7551 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
7552 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
ab47f960 7553 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 7554
10d670a3 7555 output = resolvectl('dns', 'veth-peer')
4a906586
YW
7556 print(output)
7557 self.assertRegex(output, '2001:db8:1:1::2')
7558
10d670a3 7559 output = resolvectl('domain', 'veth-peer')
4a906586
YW
7560 print(output)
7561 self.assertIn('example.com', output)
7562
10d670a3 7563 check_json(networkctl_json())
94f0bd62 7564
10d670a3 7565 output = networkctl_json('veth-peer')
681007ac
SS
7566 check_json(output)
7567
7568 # PREF64 or NAT64
7569 pref64 = json.loads(output)['NDisc']['PREF64'][0]
7570
7571 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
7572 self.assertEqual(prefix, '64:ff9b::')
7573
7574 prefix_length = pref64['PrefixLength']
7575 self.assertEqual(prefix_length, 96)
7576
635f2a66 7577 def test_ipv6_route_prefix_deny_list(self):
a962d857
YW
7578 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7579 '12-dummy.netdev', '25-ipv6ra-uplink.network')
635f2a66
YW
7580
7581 start_networkd()
95e1fbba 7582 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
635f2a66
YW
7583
7584 output = check_output('ip address show dev veth-peer')
7585 print(output)
7586 self.assertIn('inet6 2001:db8:0:1:', output)
7587 self.assertNotIn('inet6 2001:db8:0:2:', output)
7588
7589 output = check_output('ip -6 route show dev veth-peer')
7590 print(output)
7591 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7592 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7593 self.assertIn('2001:db0:fff::/64 via ', output)
7594 self.assertNotIn('2001:db1:fff::/64 via ', output)
7595
7596 output = check_output('ip address show dev veth99')
3c874fd7 7597 print(output)
635f2a66
YW
7598 self.assertNotIn('inet6 2001:db8:0:1:', output)
7599 self.assertIn('inet6 2001:db8:0:2:', output)
3c874fd7 7600
10d670a3 7601 output = resolvectl('dns', 'veth-peer')
4a906586
YW
7602 print(output)
7603 self.assertRegex(output, '2001:db8:1:1::2')
7604
10d670a3 7605 output = resolvectl('domain', 'veth-peer')
4a906586
YW
7606 print(output)
7607 self.assertIn('example.com', output)
7608
7db05447 7609class NetworkdMTUTests(unittest.TestCase, Utilities):
7db05447
DS
7610
7611 def setUp(self):
a962d857 7612 setup_common()
7db05447
DS
7613
7614 def tearDown(self):
a962d857 7615 tear_down_common()
7db05447
DS
7616
7617 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
7618 if not ipv6_mtu:
7619 ipv6_mtu = mtu
7620
7621 # test normal start
7622 start_networkd()
95e1fbba 7623 self.wait_online('dummy98:routable')
a962d857
YW
7624 self.check_link_attr('dummy98', 'mtu', mtu)
7625 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
7626
7627 # test normal restart
7628 restart_networkd()
95e1fbba 7629 self.wait_online('dummy98:routable')
a962d857
YW
7630 self.check_link_attr('dummy98', 'mtu', mtu)
7631 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
7632
7633 if reset:
7634 self.reset_check_mtu(mtu, ipv6_mtu)
7635
7636 def reset_check_mtu(self, mtu, ipv6_mtu=None):
7637 ''' test setting mtu/ipv6_mtu with interface already up '''
7638 stop_networkd()
7639
7640 # note - changing the device mtu resets the ipv6 mtu
a962d857
YW
7641 check_output('ip link set up mtu 1501 dev dummy98')
7642 check_output('ip link set up mtu 1500 dev dummy98')
7643 self.check_link_attr('dummy98', 'mtu', '1500')
7644 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7db05447
DS
7645
7646 self.check_mtu(mtu, ipv6_mtu, reset=False)
7647
7648 def test_mtu_network(self):
a962d857 7649 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7db05447
DS
7650 self.check_mtu('1600')
7651
7652 def test_mtu_netdev(self):
a962d857 7653 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
7db05447
DS
7654 # note - MTU set by .netdev happens ONLY at device creation!
7655 self.check_mtu('1600', reset=False)
7656
7657 def test_mtu_link(self):
a962d857 7658 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
7db05447
DS
7659 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7660 self.check_mtu('1600', reset=False)
7661
7662 def test_ipv6_mtu(self):
7663 ''' set ipv6 mtu without setting device mtu '''
a962d857 7664 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7db05447
DS
7665 self.check_mtu('1500', '1400')
7666
7667 def test_ipv6_mtu_toolarge(self):
7668 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
a962d857 7669 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7670 self.check_mtu('1500', '1500')
7671
7672 def test_mtu_network_ipv6_mtu(self):
7673 ''' set ipv6 mtu and set device mtu via network file '''
a962d857 7674 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7675 self.check_mtu('1600', '1550')
7676
7677 def test_mtu_netdev_ipv6_mtu(self):
7678 ''' set ipv6 mtu and set device mtu via netdev file '''
a962d857 7679 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7680 self.check_mtu('1600', '1550', reset=False)
7681
7682 def test_mtu_link_ipv6_mtu(self):
7683 ''' set ipv6 mtu and set device mtu via link file '''
a962d857 7684 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
7685 self.check_mtu('1600', '1550', reset=False)
7686
7687
1f0e3109 7688if __name__ == '__main__':
9c1ae484
YW
7689 parser = argparse.ArgumentParser()
7690 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
33b0e0c0 7691 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
9c1ae484
YW
7692 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
7693 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
94c03122 7694 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
fa4c6095 7695 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
94c03122 7696 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
6c9efba6 7697 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 7698 ns, unknown_args = parser.parse_known_args(namespace=unittest)
9c1ae484
YW
7699
7700 if ns.build_dir:
9c1ae484 7701 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
b6d587d1 7702 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
b05c4d6b 7703 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
9c1ae484
YW
7704 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
7705 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
b6d587d1
YW
7706 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
7707 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
b05c4d6b 7708 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
f66045c7 7709 build_dir = ns.build_dir
9c1ae484 7710
33b0e0c0 7711 if ns.source_dir:
f66045c7 7712 source_dir = ns.source_dir
33b0e0c0 7713 else:
f66045c7
YW
7714 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
7715 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 7716
9c1ae484
YW
7717 use_valgrind = ns.use_valgrind
7718 enable_debug = ns.enable_debug
94c03122 7719 asan_options = ns.asan_options
fa4c6095 7720 lsan_options = ns.lsan_options
94c03122 7721 ubsan_options = ns.ubsan_options
6c9efba6 7722 with_coverage = ns.with_coverage
9c1ae484
YW
7723
7724 if use_valgrind:
b05c4d6b
YW
7725 # Do not forget the trailing space.
7726 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7727
7728 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
7729 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
7730 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
7731 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
7732 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
9c1ae484 7733
9dcdf16b
YW
7734 if build_dir:
7735 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
7736 else:
7737 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
7738
94c03122 7739 if asan_options:
a962d857 7740 env.update({'ASAN_OPTIONS': asan_options})
fa4c6095 7741 if lsan_options:
a962d857 7742 env.update({'LSAN_OPTIONS': lsan_options})
94c03122 7743 if ubsan_options:
a962d857 7744 env.update({'UBSAN_OPTIONS': ubsan_options})
b05c4d6b
YW
7745 if use_valgrind:
7746 env.update({'SYSTEMD_MEMPOOL': '0'})
9c1ae484 7747
163d095f
YW
7748 wait_online_env = env.copy()
7749 if enable_debug:
a962d857 7750 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
163d095f 7751
a561bcee 7752 sys.argv[1:] = unknown_args
0765763e 7753 unittest.main(verbosity=3)