]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/test-network/systemd-networkd-tests.py
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[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
356e9bc0 10# function called test_ipv6_mtu(). To run just that test use:
d4c8de21 11#
356e9bc0 12# run0 ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
d4c8de21 13#
356e9bc0 14# Similarly, other individual tests can be run, e.g.:
d4c8de21 15#
356e9bc0
YW
16# run0 ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
17#
18# To run the test with the executables (systemd-networkd, networkctl, systemd-udevd and so on)
19# in your build directory, --build-dir=/path/to/build/ option can be used:
20#
21# run0 ./systemd-networkd-tests.py --build-dir=/path/to/build NetworkdNetworkTests.test_address_static
22#
23# Note, unlike the long getopt option handling, the path must be specified after '=', rather than space.
24# Otherwise the path is recognized as a test case, and the test run will fail.
0f1853e2 25
9c1ae484 26import argparse
b65c5390 27import datetime
7957154e 28import enum
dded88ac 29import errno
7c0d36ff 30import itertools
7957154e 31import ipaddress
aca99a3a 32import json
1f0e3109 33import os
a962d857 34import pathlib
6d1cea7b 35import random
201bf07f 36import re
1f0e3109
SS
37import shutil
38import signal
681007ac 39import socket
7957154e 40import struct
a9bc5e37
YW
41import subprocess
42import sys
912a4857 43import tempfile
a9bc5e37
YW
44import time
45import unittest
a962d857 46
ef11b841
FS
47import psutil
48
a962d857
YW
49network_unit_dir = '/run/systemd/network'
50networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
51networkd_ci_temp_dir = '/run/networkd-ci'
52udev_rules_dir = '/run/udev/rules.d'
fa724cd5 53credstore_dir = '/run/credstore'
a962d857
YW
54
55dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
56dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
57dnsmasq_lease_file = '/run/networkd-ci/test-dnsmasq.lease'
58
59isc_dhcpd_pid_file = '/run/networkd-ci/test-isc-dhcpd.pid'
60isc_dhcpd_lease_file = '/run/networkd-ci/test-isc-dhcpd.lease'
61
c1dd58b3
FS
62radvd_pid_file = '/run/networkd-ci/test-radvd.pid'
63
a962d857
YW
64systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
65which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
66
67networkd_bin = shutil.which('systemd-networkd', path=which_paths)
68resolved_bin = shutil.which('systemd-resolved', path=which_paths)
b05c4d6b 69timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
a962d857
YW
70wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
71networkctl_bin = shutil.which('networkctl', path=which_paths)
72resolvectl_bin = shutil.which('resolvectl', path=which_paths)
73timedatectl_bin = shutil.which('timedatectl', path=which_paths)
b05c4d6b 74udevadm_bin = shutil.which('udevadm', path=which_paths)
9dcdf16b 75test_ndisc_send = None
f66045c7
YW
76build_dir = None
77source_dir = None
a962d857
YW
78
79use_valgrind = False
b05c4d6b 80valgrind_cmd = ''
a962d857 81enable_debug = True
9c1ae484 82env = {}
163d095f 83wait_online_env = {}
a962d857
YW
84asan_options = None
85lsan_options = None
86ubsan_options = None
87with_coverage = False
e92d7b7d 88show_journal = True # When true, show journal on stopping networkd.
a962d857
YW
89
90active_units = []
91protected_links = {
92 'erspan0',
93 'gre0',
94 'gretap0',
95 'ifb0',
96 'ifb1',
97 'ip6_vti0',
98 'ip6gre0',
99 'ip6tnl0',
100 'ip_vti0',
101 'lo',
102 'sit0',
103 'tunl0',
104}
105saved_routes = None
106saved_ipv4_rules = None
107saved_ipv6_rules = None
f54dce2d 108saved_timezone = None
a962d857
YW
109
110def rm_f(path):
111 if os.path.exists(path):
112 os.remove(path)
113
114def rm_rf(path):
115 shutil.rmtree(path, ignore_errors=True)
116
117def cp(src, dst):
118 shutil.copy(src, dst)
119
120def cp_r(src, dst):
6b572e88 121 shutil.copytree(src, dst, copy_function=shutil.copy, dirs_exist_ok=True)
a962d857
YW
122
123def mkdir_p(path):
124 os.makedirs(path, exist_ok=True)
125
126def touch(path):
127 pathlib.Path(path).touch()
128
f9073c24 129# pylint: disable=R1710
66504b22 130def check_output(*command, **kwargs):
a962d857
YW
131 # This checks the result and returns stdout (and stderr) on success.
132 command = command[0].split() + list(command[1:])
17479d51
YW
133 ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
134 if ret.returncode == 0:
135 return ret.stdout.rstrip()
136 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
137 print(ret.stdout)
138 ret.check_returncode()
2225e7fd 139
66504b22 140def call(*command, **kwargs):
b5dac5b0 141 # This returns returncode. stdout and stderr are merged and shown in console
371810d1 142 command = command[0].split() + list(command[1:])
66504b22 143 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode
371810d1 144
7e107bc3
FS
145def call_check(*command, **kwargs):
146 # Same as call() above, but it triggers CalledProcessError if rc != 0
147 command = command[0].split() + list(command[1:])
148 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).check_returncode()
149
66504b22 150def call_quiet(*command, **kwargs):
371810d1 151 command = command[0].split() + list(command[1:])
66504b22 152 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
371810d1 153
66504b22 154def run(*command, **kwargs):
a962d857 155 # This returns CompletedProcess instance.
371810d1 156 command = command[0].split() + list(command[1:])
66504b22 157 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
371810d1 158
aca99a3a
FS
159def check_json(string):
160 try:
161 json.loads(string)
162 except json.JSONDecodeError:
163 print(f"String is not a valid JSON: '{string}'")
164 raise
165
59edcf2b
YW
166def is_module_available(*module_names):
167 for module_name in module_names:
168 lsmod_output = check_output('lsmod')
169 module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
170 if not module_re.search(lsmod_output) and call_quiet('modprobe', module_name) != 0:
171 return False
172 return True
173
174def expectedFailureIfModuleIsNotAvailable(*module_names):
7a0a37b2 175 def f(func):
59edcf2b 176 return func if is_module_available(*module_names) else unittest.expectedFailure(func)
7a0a37b2
EV
177
178 return f
179
2f0260c1
YW
180def expectedFailureIfERSPANv0IsNotSupported():
181 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
7bea7f9b 182 def f(func):
a962d857
YW
183 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')
184 remove_link('erspan99')
185 return func if rc == 0 else unittest.expectedFailure(func)
2f0260c1
YW
186
187 return f
188
189def expectedFailureIfERSPANv2IsNotSupported():
190 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
191 def f(func):
a962d857
YW
192 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')
193 remove_link('erspan99')
194 return func if rc == 0 else unittest.expectedFailure(func)
7bea7f9b
SS
195
196 return f
197
d586a2c3
YW
198def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
199 def f(func):
a962d857
YW
200 rc = call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
201 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
202 return func if rc == 0 else unittest.expectedFailure(func)
d586a2c3
YW
203
204 return f
205
206def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
207 def f(func):
24e37929
YW
208 # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols.
209 # Hence. here we use explicit number: 6 == tcp.
210 rc = call_quiet('ip rule add not from 192.168.100.19 ipproto 6 table 7')
211 call_quiet('ip rule del not from 192.168.100.19 ipproto 6 table 7')
a962d857 212 return func if rc == 0 else unittest.expectedFailure(func)
d586a2c3
YW
213
214 return f
215
6be8e78e
YW
216def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
217 def f(func):
a962d857
YW
218 supported = False
219 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
220 ret = run('ip rule list from 192.168.100.19 table 7')
221 supported = ret.returncode == 0 and 'uidrange 200-300' in ret.stdout
222 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
223 return func if supported else unittest.expectedFailure(func)
6be8e78e
YW
224
225 return f
226
4be1fc84
NC
227def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
228 def f(func):
229 rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev')
230 call_quiet('ip rule del not from 192.168.100.19 l3mdev')
231 return func if rc == 0 else unittest.expectedFailure(func)
232
233 return f
234
086bcf5d
YW
235def expectedFailureIfNexthopIsNotAvailable():
236 def f(func):
a962d857
YW
237 rc = call_quiet('ip nexthop list')
238 return func if rc == 0 else unittest.expectedFailure(func)
086bcf5d
YW
239
240 return f
241
297f9d86
YW
242def expectedFailureIfRTA_VIAIsNotSupported():
243 def f(func):
a962d857
YW
244 call_quiet('ip link add dummy98 type dummy')
245 call_quiet('ip link set up dev dummy98')
246 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
247 rc = call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
248 remove_link('dummy98')
249 return func if rc == 0 else unittest.expectedFailure(func)
297f9d86
YW
250
251 return f
252
6934ace0
YW
253def expectedFailureIfAlternativeNameIsNotAvailable():
254 def f(func):
a962d857
YW
255 call_quiet('ip link add dummy98 type dummy')
256 supported = \
257 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
258 call_quiet('ip link show dev hogehogehogehogehoge') == 0
259 remove_link('dummy98')
260 return func if supported else unittest.expectedFailure(func)
6934ace0
YW
261
262 return f
263
3d2c2692
YW
264def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
265 def f(func):
a962d857
YW
266 def finalize(func, supported):
267 call_quiet('rmmod netdevsim')
268 return func if supported else unittest.expectedFailure(func)
269
270 call_quiet('rmmod netdevsim')
271 if call_quiet('modprobe netdevsim') != 0:
272 return finalize(func, False)
3d2c2692
YW
273
274 try:
d45476ef 275 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
3d2c2692 276 f.write('99 1')
54e2f32f 277 except OSError:
a962d857 278 return finalize(func, False)
3d2c2692 279
d8746f16 280 return finalize(func, os.path.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
3d2c2692
YW
281
282 return f
283
d22f2fb9
YW
284def expectedFailureIfKernelReturnsInvalidFlags():
285 '''
45af01d3
YW
286 This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
287 fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
d22f2fb9
YW
288 '''
289 def f(func):
290 call_quiet('ip link add dummy98 type dummy')
291 call_quiet('ip link set up dev dummy98')
292 call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute')
293 output = check_output('ip address show dev dummy98')
294 remove_link('dummy98')
295 return func if 'noprefixroute' in output else unittest.expectedFailure(func)
296
297 return f
298
b3de9d7b
FS
299# pylint: disable=C0415
300def compare_kernel_version(min_kernel_version):
301 try:
302 import platform
303 from packaging import version
304 except ImportError:
305 print('Failed to import either platform or packaging module, assuming the comparison failed')
306 return False
307
308 # Get only the actual kernel version without any build/distro/arch stuff
309 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
310 kver = platform.release().split('-')[0]
0e808f62
TM
311 # Get also rid of '+'
312 kver = kver.split('+')[0]
b3de9d7b
FS
313
314 return version.parse(kver) >= version.parse(min_kernel_version)
315
a962d857
YW
316def copy_network_unit(*units, copy_dropins=True):
317 """
318 Copy networkd unit files into the testbed.
1f0e3109 319
a962d857 320 Any networkd unit file type can be specified, as well as drop-in files.
1f0e3109 321
a962d857
YW
322 By default, all drop-ins for a specified unit file are copied in;
323 to avoid that specify dropins=False.
3e3b0d2a 324
a962d857
YW
325 When a drop-in file is specified, its unit file is also copied in automatically.
326 """
32ab27af 327 has_link = False
a962d857
YW
328 mkdir_p(network_unit_dir)
329 for unit in units:
330 if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
331 cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
32ab27af 332
a962d857
YW
333 if unit.endswith('.conf'):
334 dropin = unit
335 unit = os.path.dirname(dropin).rstrip('.d')
336 dropindir = os.path.join(network_unit_dir, unit + '.d')
337 mkdir_p(dropindir)
338 cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
32ab27af 339
a962d857
YW
340 cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
341
32ab27af
YW
342 if unit.endswith('.link'):
343 has_link = True
344
345 if has_link:
a39a2a81 346 udevadm_reload()
32ab27af 347
fa724cd5
MY
348def copy_credential(src, target):
349 mkdir_p(credstore_dir)
350 cp(os.path.join(networkd_ci_temp_dir, src),
351 os.path.join(credstore_dir, target))
352
a962d857
YW
353def remove_network_unit(*units):
354 """
355 Remove previously copied unit files from the testbed.
356
357 Drop-ins will be removed automatically.
358 """
32ab27af 359 has_link = False
a962d857
YW
360 for unit in units:
361 rm_f(os.path.join(network_unit_dir, unit))
362 rm_rf(os.path.join(network_unit_dir, unit + '.d'))
363
32ab27af
YW
364 if unit.endswith('.link') or unit.endswith('.link.d'):
365 has_link = True
366
367 if has_link:
a39a2a81 368 udevadm_reload()
32ab27af 369
0de55624
YW
370def touch_network_unit(*units):
371 for unit in units:
372 touch(os.path.join(network_unit_dir, unit))
373
a962d857 374def clear_network_units():
32ab27af
YW
375 has_link = False
376 if os.path.exists(network_unit_dir):
377 units = os.listdir(network_unit_dir)
378 for unit in units:
379 if unit.endswith('.link') or unit.endswith('.link.d'):
380 has_link = True
381
a962d857
YW
382 rm_rf(network_unit_dir)
383
32ab27af 384 if has_link:
a39a2a81 385 udevadm_reload()
32ab27af 386
a962d857
YW
387def copy_networkd_conf_dropin(*dropins):
388 """Copy networkd.conf dropin files into the testbed."""
389 mkdir_p(networkd_conf_dropin_dir)
390 for dropin in dropins:
391 cp(os.path.join(networkd_ci_temp_dir, dropin), networkd_conf_dropin_dir)
392
393def remove_networkd_conf_dropin(*dropins):
394 """Remove previously copied networkd.conf dropin files from the testbed."""
395 for dropin in dropins:
396 rm_f(os.path.join(networkd_conf_dropin_dir, dropin))
397
398def clear_networkd_conf_dropins():
399 rm_rf(networkd_conf_dropin_dir)
400
33b0e0c0 401def setup_systemd_udev_rules():
f2adc1de 402 if not build_dir and not source_dir:
33b0e0c0
FS
403 return
404
405 mkdir_p(udev_rules_dir)
406
f66045c7 407 for path in [build_dir, source_dir]:
f2adc1de
DDM
408 if not path:
409 continue
410
f66045c7 411 path = os.path.join(path, "rules.d")
33b0e0c0
FS
412 print(f"Copying udev rules from {path} to {udev_rules_dir}")
413
414 for rule in os.listdir(path):
415 if not rule.endswith(".rules"):
416 continue
417 cp(os.path.join(path, rule), udev_rules_dir)
418
c84a5f5e
YW
419def clear_networkd_state_files():
420 rm_rf('/var/lib/systemd/network/')
421
a962d857
YW
422def copy_udev_rule(*rules):
423 """Copy udev rules"""
424 mkdir_p(udev_rules_dir)
425 for rule in rules:
426 cp(os.path.join(networkd_ci_temp_dir, rule), udev_rules_dir)
427
428def remove_udev_rule(*rules):
429 """Remove previously copied udev rules"""
430 for rule in rules:
431 rm_f(os.path.join(udev_rules_dir, rule))
432
433def clear_udev_rules():
434 rm_rf(udev_rules_dir)
435
436def save_active_units():
87b308c8 437 for u in ['systemd-networkd.socket', 'systemd-networkd.service',
b05c4d6b 438 'systemd-resolved.service', 'systemd-timesyncd.service',
f7ada4b8 439 'firewalld.service']:
2225e7fd 440 if call(f'systemctl is-active --quiet {u}') == 0:
a962d857
YW
441 call(f'systemctl stop {u}')
442 active_units.append(u)
443
444def restore_active_units():
445 if 'systemd-networkd.socket' in active_units:
446 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
a962d857
YW
447 for u in active_units:
448 call(f'systemctl restart {u}')
449
93f5ae6b
YW
450def create_unit_dropin(unit, contents):
451 mkdir_p(f'/run/systemd/system/{unit}.d')
452 with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
453 f.write('\n'.join(contents))
454
62eaf8d0 455def create_service_dropin(service, command, additional_settings=None):
c84a5f5e
YW
456 drop_in = ['[Service]']
457 if command:
458 drop_in += [
459 'ExecStart=',
51e60dac 460 f'ExecStart={valgrind_cmd}{command}',
c84a5f5e 461 ]
b05c4d6b
YW
462 if enable_debug:
463 drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
464 if asan_options:
465 drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
466 if lsan_options:
467 drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
468 if ubsan_options:
469 drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
470 if asan_options or lsan_options or ubsan_options:
471 drop_in += ['SystemCallFilter=']
472 if use_valgrind or asan_options or lsan_options or ubsan_options:
473 drop_in += ['MemoryDenyWriteExecute=no']
474 if use_valgrind:
475 drop_in += [
476 'Environment=SYSTEMD_MEMPOOL=0',
477 'PrivateTmp=yes',
478 ]
479 if with_coverage:
480 drop_in += [
481 'ProtectSystem=no',
482 'ProtectHome=no',
483 ]
484 if additional_settings:
485 drop_in += additional_settings
486
93f5ae6b 487 create_unit_dropin(f'{service}.service', drop_in)
b05c4d6b 488
83cc1825 489def setup_system_units():
f2adc1de 490 if build_dir or source_dir:
83cc1825
YW
491 mkdir_p('/run/systemd/system/')
492
493 for unit in [
494 'systemd-networkd.service',
495 'systemd-networkd.socket',
c84a5f5e 496 'systemd-networkd-persistent-storage.service',
83cc1825
YW
497 'systemd-resolved.service',
498 'systemd-timesyncd.service',
499 'systemd-udevd.service',
500 ]:
501 for path in [build_dir, source_dir]:
f2adc1de
DDM
502 if not path:
503 continue
504
83cc1825
YW
505 fullpath = os.path.join(os.path.join(path, "units"), unit)
506 if os.path.exists(fullpath):
507 print(f"Copying unit file from {fullpath} to /run/systemd/system/")
508 cp(fullpath, '/run/systemd/system/')
509 break
510
511 create_service_dropin('systemd-networkd', networkd_bin,
512 ['[Service]',
513 'Restart=no',
514 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
515 '[Unit]',
516 'StartLimitIntervalSec=0'])
517 create_service_dropin('systemd-resolved', resolved_bin)
518 create_service_dropin('systemd-timesyncd', timesyncd_bin)
519
520 # TODO: also run udevd with sanitizers, valgrind, or coverage
521 create_unit_dropin(
522 'systemd-udevd.service',
523 [
524 '[Service]',
525 'ExecStart=',
51e60dac 526 f'ExecStart=@{udevadm_bin} systemd-udevd',
83cc1825
YW
527 ]
528 )
529 create_unit_dropin(
530 'systemd-networkd.socket',
531 [
532 '[Unit]',
533 'StartLimitIntervalSec=0',
534 ]
535 )
c84a5f5e
YW
536 create_unit_dropin(
537 'systemd-networkd-persistent-storage.service',
538 [
539 '[Unit]',
540 'StartLimitIntervalSec=0',
541 '[Service]',
542 'ExecStart=',
543 f'ExecStart={networkctl_bin} persistent-storage yes',
544 'ExecStop=',
22fa8f67
YW
545 f'ExecStop={networkctl_bin} persistent-storage no',
546 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '',
c84a5f5e
YW
547 ]
548 )
83cc1825
YW
549
550 check_output('systemctl daemon-reload')
551 print(check_output('systemctl cat systemd-networkd.service'))
c84a5f5e 552 print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
83cc1825
YW
553 print(check_output('systemctl cat systemd-resolved.service'))
554 print(check_output('systemctl cat systemd-timesyncd.service'))
555 print(check_output('systemctl cat systemd-udevd.service'))
556 check_output('systemctl restart systemd-resolved.service')
557 check_output('systemctl restart systemd-timesyncd.service')
558 check_output('systemctl restart systemd-udevd.service')
559
560def clear_system_units():
561 def rm_unit(name):
562 rm_f(f'/run/systemd/system/{name}')
563 rm_rf(f'/run/systemd/system/{name}.d')
564
565 rm_unit('systemd-networkd.service')
566 rm_unit('systemd-networkd.socket')
c84a5f5e 567 rm_unit('systemd-networkd-persistent-storage.service')
83cc1825
YW
568 rm_unit('systemd-resolved.service')
569 rm_unit('systemd-timesyncd.service')
570 rm_unit('systemd-udevd.service')
571 check_output('systemctl daemon-reload')
572 check_output('systemctl restart systemd-udevd.service')
573
acc06d8a
YW
574def link_exists(*links):
575 for link in links:
576 if call_quiet(f'ip link show {link}') != 0:
577 return False
578 return True
a962d857 579
b95d35b5 580def link_resolve(link):
f1f1be71 581 return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0]
a962d857
YW
582
583def remove_link(*links, protect=False):
584 for link in links:
585 if protect and link in protected_links:
586 continue
587 if link_exists(link):
588 call(f'ip link del dev {link}')
589
590def save_existing_links():
591 links = os.listdir('/sys/class/net')
592 for link in links:
593 if link_exists(link):
594 protected_links.add(link)
595
596 print('### The following links will be protected:')
597 print(', '.join(sorted(list(protected_links))))
598
dadf2bd4
YW
599def unmanage_existing_links():
600 mkdir_p(network_unit_dir)
601
602 with open(os.path.join(network_unit_dir, '00-unmanaged.network'), mode='w', encoding='utf-8') as f:
603 f.write('[Match]\n')
604 for link in protected_links:
605 f.write(f'Name={link}\n')
606 f.write('\n[Link]\nUnmanaged=yes\n')
607
a962d857
YW
608def flush_links():
609 links = os.listdir('/sys/class/net')
610 remove_link(*links, protect=True)
611
612def flush_nexthops():
613 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
614 # Hence, we cannot restore nexthops in a simple way.
615 # Let's assume there is no nexthop used in the system
616 call_quiet('ip nexthop flush')
617
618def save_routes():
619 # pylint: disable=global-statement
620 global saved_routes
621 saved_routes = check_output('ip route show table all')
622 print('### The following routes will be protected:')
623 print(saved_routes)
624
625def flush_routes():
626 have = False
627 output = check_output('ip route show table all')
628 for line in output.splitlines():
629 if line in saved_routes:
630 continue
631 if 'proto kernel' in line:
632 continue
633 if ' dev ' in line and not ' dev lo ' in line:
634 continue
635 if not have:
636 have = True
637 print('### Removing routes that did not exist when the test started.')
638 print(f'# {line}')
639 call(f'ip route del {line}')
640
641def save_routing_policy_rules():
642 # pylint: disable=global-statement
643 global saved_ipv4_rules, saved_ipv6_rules
644 def save(ipv):
645 output = check_output(f'ip -{ipv} rule show')
646 print(f'### The following IPv{ipv} routing policy rules will be protected:')
647 print(output)
648 return output
649
650 saved_ipv4_rules = save(4)
651 saved_ipv6_rules = save(6)
652
653def flush_routing_policy_rules():
654 def flush(ipv, saved_rules):
655 have = False
656 output = check_output(f'ip -{ipv} rule show')
657 for line in output.splitlines():
658 if line in saved_rules:
659 continue
660 if not have:
661 have = True
662 print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
663 print(f'# {line}')
936dec43 664 words = line.replace('lookup [l3mdev-table]', 'l3mdev').replace('[detached]', '').split()
a962d857
YW
665 priority = words[0].rstrip(':')
666 call(f'ip -{ipv} rule del priority {priority} ' + ' '.join(words[1:]))
667
668 flush(4, saved_ipv4_rules)
669 flush(6, saved_ipv6_rules)
670
671def flush_fou_ports():
672 ret = run('ip fou show')
673 if ret.returncode != 0:
674 return # fou may not be supported
675 for line in ret.stdout.splitlines():
676 port = line.split()[1]
677 call(f'ip fou del port {port}')
678
679def flush_l2tp_tunnels():
e11d0e39 680 tids = []
49ad2872
YW
681 ret = run('ip l2tp show tunnel')
682 if ret.returncode != 0:
683 return # l2tp may not be supported
684 for line in ret.stdout.splitlines():
a962d857
YW
685 words = line.split()
686 if words[0] == 'Tunnel':
687 tid = words[1].rstrip(',')
688 call(f'ip l2tp del tunnel tunnel_id {tid}')
e11d0e39
YW
689 tids.append(tid)
690
691 # Removing L2TP tunnel is asynchronous and slightly takes a time.
692 for tid in tids:
693 for _ in range(50):
694 r = run(f'ip l2tp show tunnel tunnel_id {tid}')
695 if r.returncode != 0 or len(r.stdout.rstrip()) == 0:
696 break
697 time.sleep(.2)
698 else:
699 print(f'Cannot remove L2TP tunnel {tid}, ignoring.')
a962d857 700
f54dce2d 701def save_timezone():
f9073c24 702 # pylint: disable=global-statement
f54dce2d 703 global saved_timezone
67a9b3ec 704 r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
f54dce2d
YW
705 if r.returncode == 0:
706 saved_timezone = r.stdout.rstrip()
707 print(f'### Saved timezone: {saved_timezone}')
708
709def restore_timezone():
710 if saved_timezone:
67a9b3ec 711 call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
f54dce2d 712
a962d857
YW
713def read_link_attr(*args):
714 with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
715 return f.readline().strip()
716
34290c6a
YW
717def read_manager_state_file():
718 with open('/run/systemd/netif/state', encoding='utf-8') as f:
719 return f.read()
720
a962d857
YW
721def read_link_state_file(link):
722 ifindex = read_link_attr(link, 'ifindex')
723 path = os.path.join('/run/systemd/netif/links', ifindex)
724 with open(path, encoding='utf-8') as f:
725 return f.read()
726
727def read_ip_sysctl_attr(link, attribute, ipv):
728 with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f:
729 return f.readline().strip()
730
d4c8de21
MM
731def read_ip_neigh_sysctl_attr(link, attribute, ipv):
732 with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f:
733 return f.readline().strip()
734
a962d857
YW
735def read_ipv6_sysctl_attr(link, attribute):
736 return read_ip_sysctl_attr(link, attribute, 'ipv6')
737
d4c8de21
MM
738def read_ipv6_neigh_sysctl_attr(link, attribute):
739 return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6')
740
a962d857
YW
741def read_ipv4_sysctl_attr(link, attribute):
742 return read_ip_sysctl_attr(link, attribute, 'ipv4')
743
af44a16e
YW
744def read_mpls_sysctl_attr(link, attribute):
745 return read_ip_sysctl_attr(link, attribute, 'mpls')
746
c1dd58b3
FS
747def stop_by_pid_file(pid_file):
748 if not os.path.exists(pid_file):
749 return
750 with open(pid_file, 'r', encoding='utf-8') as f:
751 pid = f.read().rstrip(' \t\r\n\0')
752 os.kill(int(pid), signal.SIGTERM)
753 for _ in range(25):
754 try:
755 os.kill(int(pid), 0)
756 print(f"PID {pid} is still alive, waiting...")
757 time.sleep(.2)
758 except OSError as e:
759 if e.errno == errno.ESRCH:
760 break
761 print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
762 rm_f(pid_file)
763
7957154e
RP
764def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
765 b = bytes()
766 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
767 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
768 ipv4 = ipaddress.IPv4Address
769 class SvcParam(enum.Enum):
770 ALPN = 1
771 DOHPATH = 7
772
773 data = pyton(prio)
774
775 adn = adn.rstrip('.') + '.'
776 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')))
777
778 if not addrs: # adn-only mode
779 return pack(data, 2)
780
781 data += pack(b.join(ipv4(addr).packed for addr in addrs))
782 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
783 if dohpath is not None:
784 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
785
786 return pack(data, 2)
787
cb386795
RP
788def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
789 b = bytes()
790 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
791 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
792 ipv6 = ipaddress.IPv6Address
793 class SvcParam(enum.Enum):
794 ALPN = 1
795 DOHPATH = 7
796
797 data = pyton(prio)
798
799 adn = adn.rstrip('.') + '.'
800 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')), 2)
801
802 if not addrs: # adn-only mode
803 return data
804
805 data += pack(b.join(ipv6(addr).packed for addr in addrs), 2)
806 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
807 if dohpath is not None:
808 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
809
810 return data
811
2d7ca6b4
YW
812def 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'):
813 if ra_mode:
814 ra_mode = f',{ra_mode}'
815 else:
816 ra_mode = ''
817
a962d857
YW
818 command = (
819 'dnsmasq',
820 f'--log-facility={dnsmasq_log_file}',
821 '--log-queries=extra',
822 '--log-dhcp',
823 f'--pid-file={dnsmasq_pid_file}',
824 '--conf-file=/dev/null',
825 '--bind-interfaces',
826 f'--interface={interface}',
827 f'--dhcp-leasefile={dnsmasq_lease_file}',
828 '--enable-ra',
2d7ca6b4
YW
829 f'--dhcp-range={ipv6_range}{ra_mode},2m',
830 f'--dhcp-range={ipv4_range},2m',
a962d857
YW
831 '--dhcp-option=option:mtu,1492',
832 f'--dhcp-option=option:router,{ipv4_router}',
833 '--port=0',
834 '--no-resolv',
835 ) + additional_options
836 check_output(*command)
837
a962d857
YW
838def stop_dnsmasq():
839 stop_by_pid_file(dnsmasq_pid_file)
840 rm_f(dnsmasq_lease_file)
841 rm_f(dnsmasq_log_file)
842
843def read_dnsmasq_log_file():
844 with open(dnsmasq_log_file, encoding='utf-8') as f:
845 return f.read()
846
847def start_isc_dhcpd(conf_file, ipv, interface='veth-peer'):
848 conf_file_path = os.path.join(networkd_ci_temp_dir, conf_file)
849 isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
850 touch(isc_dhcpd_lease_file)
851 check_output(isc_dhcpd_command)
852
853def stop_isc_dhcpd():
854 stop_by_pid_file(isc_dhcpd_pid_file)
855 rm_f(isc_dhcpd_lease_file)
856
e081ffc1
YW
857def get_dbus_link_path(link):
858 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
859 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
860 'GetLinkByName', 's', link])
861
862 assert out.startswith(b'io ')
863 out = out.strip()
864 assert out.endswith(b'"')
865 out = out.decode()
866 return out[:-1].split('"')[1]
867
868def get_dhcp_client_state(link, family):
869 link_path = get_dbus_link_path(link)
870
871 out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
872 link_path, f'org.freedesktop.network1.DHCPv{family}Client', 'State'])
873 assert out.startswith(b's "')
874 out = out.strip()
875 assert out.endswith(b'"')
876 return out[3:-1].decode()
877
878def get_dhcp4_client_state(link):
879 return get_dhcp_client_state(link, '4')
880
881def get_dhcp6_client_state(link):
882 return get_dhcp_client_state(link, '6')
883
884def get_link_description(link):
885 link_path = get_dbus_link_path(link)
886
887 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
888 link_path, 'org.freedesktop.network1.Link', 'Describe'])
889 assert out.startswith(b's "')
890 out = out.strip()
891 assert out.endswith(b'"')
892 json_raw = out[2:].decode()
893 check_json(json_raw)
894 description = json.loads(json_raw) # Convert from escaped sequences to json
895 check_json(description)
896 return json.loads(description) # Now parse the json
897
c1dd58b3
FS
898def start_radvd(*additional_options, config_file):
899 config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file)
900 command = (
901 'radvd',
902 f'--pidfile={radvd_pid_file}',
903 f'--config={config_file_path}',
904 '--logmethod=stderr',
905 ) + additional_options
906 check_output(*command)
907
908def stop_radvd():
909 stop_by_pid_file(radvd_pid_file)
910
911def radvd_check_config(config_file):
912 if not shutil.which('radvd'):
913 print('radvd is not installed, assuming the config check failed')
914 return False
915
916 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
917 # set up (one instance is @unittest.skipX())
918 config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file)
919 return call(f'radvd --config={config_file_path} --configtest') == 0
920
5bd2a7c5
YW
921def networkd_invocation_id():
922 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
923
653c38b3
YW
924def networkd_pid():
925 return check_output('systemctl show --value -p MainPID systemd-networkd.service')
926
b65c5390 927def read_networkd_log(invocation_id=None, since=None):
5bd2a7c5
YW
928 if not invocation_id:
929 invocation_id = networkd_invocation_id()
b65c5390
YW
930 command = [
931 'journalctl',
dc60ac29
YW
932 '--no-hostname',
933 '--output=short-monotonic',
b65c5390
YW
934 f'_SYSTEMD_INVOCATION_ID={invocation_id}',
935 ]
936 if since:
937 command.append(f'--since={since}')
bd581438 938 check_output('journalctl --sync')
b65c5390 939 return check_output(*command)
5bd2a7c5 940
6b07675d
YW
941def networkd_is_failed():
942 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
943
456727b5 944def stop_networkd(show_logs=True, check_failed=True):
e92d7b7d
YW
945 global show_journal
946 show_logs = show_logs and show_journal
a962d857 947 if show_logs:
5bd2a7c5 948 invocation_id = networkd_invocation_id()
456727b5
YW
949
950 if check_failed:
951 check_output('systemctl stop systemd-networkd.socket')
952 check_output('systemctl stop systemd-networkd.service')
953 else:
954 call('systemctl stop systemd-networkd.socket')
955 call('systemctl stop systemd-networkd.service')
956
a962d857 957 if show_logs:
5bd2a7c5 958 print(read_networkd_log(invocation_id))
456727b5 959
7618fd06 960 # Check if networkd exits cleanly.
456727b5
YW
961 if check_failed:
962 assert not networkd_is_failed()
a962d857
YW
963
964def start_networkd():
965 check_output('systemctl start systemd-networkd')
653c38b3
YW
966 invocation_id = networkd_invocation_id()
967 pid = networkd_pid()
968 print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
a962d857
YW
969
970def restart_networkd(show_logs=True):
e92d7b7d
YW
971 global show_journal
972 show_logs = show_logs and show_journal
85b1a14d 973 if show_logs:
5bd2a7c5 974 invocation_id = networkd_invocation_id()
85b1a14d
YW
975 check_output('systemctl restart systemd-networkd.service')
976 if show_logs:
5bd2a7c5 977 print(read_networkd_log(invocation_id))
a962d857 978
653c38b3
YW
979 invocation_id = networkd_invocation_id()
980 pid = networkd_pid()
981 print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
982
ae014ecb
YW
983def networkd_pid():
984 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
985
10d670a3 986def networkctl(*args):
6b07675d
YW
987 # Do not call networkctl if networkd is in failed state.
988 # Otherwise, networkd may be restarted and we may get wrong results.
989 assert not networkd_is_failed()
10d670a3
YW
990 return check_output(*(networkctl_cmd + list(args)), env=env)
991
992def networkctl_status(*args):
993 return networkctl('-n', '0', 'status', *args)
994
995def networkctl_json(*args):
996 return networkctl('--json=short', 'status', *args)
997
a962d857 998def networkctl_reconfigure(*links):
10d670a3 999 networkctl('reconfigure', *links)
a962d857 1000
4bc771d0 1001def networkctl_reload():
10d670a3 1002 networkctl('reload')
a962d857 1003
10d670a3
YW
1004def resolvectl(*args):
1005 return check_output(*(resolvectl_cmd + list(args)), env=env)
1006
1007def timedatectl(*args):
1008 return check_output(*(timedatectl_cmd + list(args)), env=env)
1009
a39a2a81
YW
1010def udevadm(*args):
1011 return check_output(*(udevadm_cmd + list(args)))
1012
1013def udevadm_reload():
1014 udevadm('control', '--reload')
1015
1016def udevadm_trigger(*args, action='add'):
1017 udevadm('trigger', '--settle', f'--action={action}', *args)
1018
a962d857 1019def setup_common():
dadf2bd4
YW
1020 # Protect existing links
1021 unmanage_existing_links()
1022
6fec5982
YW
1023 # We usually show something in each test. So, let's break line to make the title of a test and output
1024 # from the test mixed. Then, flush stream buffer and journals.
a962d857 1025 print()
6fec5982
YW
1026 sys.stdout.flush()
1027 check_output('journalctl --sync')
a962d857
YW
1028
1029def tear_down_common():
c1dd58b3 1030 # 1. stop DHCP/RA servers
a962d857
YW
1031 stop_dnsmasq()
1032 stop_isc_dhcpd()
c1dd58b3 1033 stop_radvd()
a962d857
YW
1034
1035 # 2. remove modules
1036 call_quiet('rmmod netdevsim')
1037 call_quiet('rmmod sch_teql')
1038
1039 # 3. remove network namespace
1040 call_quiet('ip netns del ns99')
1041
1042 # 4. remove links
e11d0e39 1043 flush_l2tp_tunnels()
a962d857
YW
1044 flush_links()
1045
1046 # 5. stop networkd
456727b5 1047 stop_networkd(check_failed=False)
a962d857
YW
1048
1049 # 6. remove configs
1050 clear_network_units()
1051 clear_networkd_conf_dropins()
c84a5f5e 1052 clear_networkd_state_files()
a962d857
YW
1053
1054 # 7. flush settings
1055 flush_fou_ports()
a962d857
YW
1056 flush_nexthops()
1057 flush_routing_policy_rules()
1058 flush_routes()
1059
6fec5982
YW
1060 # 8. flush stream buffer and journals to make not any output from the test with the next one
1061 sys.stdout.flush()
1062 check_output('journalctl --sync')
1063
456727b5
YW
1064 # 9. check the status of networkd
1065 assert not networkd_is_failed()
1066
a962d857
YW
1067def setUpModule():
1068 rm_rf(networkd_ci_temp_dir)
1069 cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir)
1070
1071 clear_network_units()
1072 clear_networkd_conf_dropins()
c84a5f5e 1073 clear_networkd_state_files()
a962d857
YW
1074 clear_udev_rules()
1075
33b0e0c0 1076 setup_systemd_udev_rules()
a962d857
YW
1077 copy_udev_rule('00-debug-net.rules')
1078
1079 # Save current state
1080 save_active_units()
1081 save_existing_links()
1082 save_routes()
1083 save_routing_policy_rules()
f54dce2d 1084 save_timezone()
c0bf6733 1085
83cc1825 1086 setup_system_units()
9c1ae484 1087
1f0e3109 1088def tearDownModule():
a962d857
YW
1089 rm_rf(networkd_ci_temp_dir)
1090 clear_udev_rules()
1091 clear_network_units()
1092 clear_networkd_conf_dropins()
c84a5f5e 1093 clear_networkd_state_files()
a962d857 1094
f54dce2d
YW
1095 restore_timezone()
1096
83cc1825 1097 clear_system_units()
a962d857 1098 restore_active_units()
ec38833c 1099
6fec5982
YW
1100 # Flush stream buffer and journals before showing the test summary.
1101 sys.stdout.flush()
1102 check_output('journalctl --sync')
1103
a962d857
YW
1104class Utilities():
1105 # pylint: disable=no-member
caad88a2 1106
acc06d8a 1107 def check_link_exists(self, *link, expected=True):
a962d857 1108 if expected:
acc06d8a 1109 self.assertTrue(link_exists(*link))
a962d857 1110 else:
acc06d8a 1111 self.assertFalse(link_exists(*link))
caad88a2 1112
a962d857
YW
1113 def check_link_attr(self, *args):
1114 self.assertEqual(read_link_attr(*args[:-1]), args[-1])
aaae5713 1115
a962d857
YW
1116 def check_bridge_port_attr(self, master, port, attribute, expected, allow_enoent=False):
1117 path = os.path.join('/sys/devices/virtual/net', master, 'lower_' + port, 'brport', attribute)
1118 if allow_enoent and not os.path.exists(path):
1119 return
1120 with open(path, encoding='utf-8') as f:
1121 self.assertEqual(f.readline().strip(), expected)
aaae5713 1122
a962d857
YW
1123 def check_ipv4_sysctl_attr(self, link, attribute, expected):
1124 self.assertEqual(read_ipv4_sysctl_attr(link, attribute), expected)
ec38833c 1125
a962d857
YW
1126 def check_ipv6_sysctl_attr(self, link, attribute, expected):
1127 self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected)
aaae5713 1128
d4c8de21
MM
1129 def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
1130 self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
1131
af44a16e
YW
1132 def check_mpls_sysctl_attr(self, link, attribute, expected):
1133 self.assertEqual(read_mpls_sysctl_attr(link, attribute), expected)
1134
7ef26afc
YW
1135 def wait_links(self, *links, trial=40):
1136 for _ in range(trial):
acc06d8a 1137 if link_exists(*links):
7ef26afc
YW
1138 break
1139 time.sleep(0.5)
1140 else:
a962d857 1141 self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links)))
0fc0d85f 1142
7ef26afc 1143 def wait_activated(self, link, state='down', trial=40):
cfbdc438 1144 # wait for the interface is activated.
cfbdc438
YW
1145 needle = f'{link}: Bringing link {state}'
1146 flag = state.upper()
7ef26afc
YW
1147 self.wait_links(link, trial=trial)
1148 self.check_networkd_log(needle, trial=trial)
1149 for _ in range(trial):
1150 if flag in check_output(f'ip link show {link}'):
1151 break
1152 time.sleep(0.5)
1153 else:
cfbdc438 1154 self.fail(f'Timed out waiting for {link} activated.')
cfbdc438 1155
a4632dc7
DS
1156 def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
1157 """Wait for the link to reach the specified operstate and/or setup state.
1158
1159 Specify None or '' for either operstate or setup_state to ignore that state.
1160 This will recheck until the state conditions are met or the timeout expires.
1161
1162 If the link successfully matches the requested state, this returns True.
1163 If this times out waiting for the link to match, the behavior depends on the
1164 'fail_assert' parameter; if True, this causes a test assertion failure,
1165 otherwise this returns False. The default is to cause assertion failure.
1166
1167 Note that this function matches on *exactly* the given operstate and setup_state.
1168 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1169 """
1170 if not operstate:
1171 operstate = r'\S+'
1172 if not setup_state:
1173 setup_state = r'\S+'
1174
7ef26afc 1175 for _ in range(setup_timeout * 2):
459c35d4 1176 if not link_exists(link):
77356099 1177 time.sleep(0.5)
459c35d4 1178 continue
10d670a3 1179 output = networkctl_status(link)
a4632dc7
DS
1180 if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
1181 return True
7ef26afc 1182 time.sleep(0.5)
894ff7d1 1183
a4632dc7
DS
1184 if fail_assert:
1185 self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1186 return False
2be0b6fc 1187
d6608da1 1188 def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5, bool_dns=False):
0923b425 1189 """Wait for the links to reach the specified operstate and/or setup state.
0c020321
DS
1190
1191 This is similar to wait_operstate() but can be used for multiple links,
1192 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1193 The operstate should be specified in the link name, like 'eth0:degraded'.
1194 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1195
1196 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1197 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1198
1199 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1200 If this is set, no setup_state checks are done.
1201
d6608da1
NR
1202 Set 'bool_dns' to True to wait for DNS servers to be accessible.
1203
70448bb1
L
1204 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1205 This is applied only for the operational state 'degraded' or above.
1206
0923b425 1207 Note that this function waits for the links to reach *or exceed* the given operstate.
0c020321
DS
1208 However, the setup_state, if specified, must be matched *exactly*.
1209
0923b425 1210 This returns if the links reached the requested operstate/setup_state; otherwise it
0c020321
DS
1211 raises CalledProcessError or fails test assertion.
1212 """
56dfde0d 1213 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
1214 if bool_any:
1215 args += ['--any']
d6608da1
NR
1216 if bool_dns:
1217 args += ['--dns']
70448bb1
L
1218 if ipv4:
1219 args += ['--ipv4']
1220 if ipv6:
1221 args += ['--ipv6']
e2aea43f 1222 try:
163d095f 1223 check_output(*args, env=wait_online_env)
f9073c24 1224 except subprocess.CalledProcessError:
6b07675d
YW
1225 if networkd_is_failed():
1226 print('!!!!! systemd-networkd.service is failed !!!!!')
1227 call('systemctl status systemd-networkd.service')
1228 else:
1229 # show detailed status on failure
1230 for link in links_with_operstate:
1231 name = link.split(':')[0]
1232 if link_exists(name):
68543224
YW
1233 print(networkctl_status(name))
1234 else:
1235 print(f'Interface {name} not found.')
e2aea43f 1236 raise
fd372b1a 1237 if not bool_any and setup_state:
0c020321
DS
1238 for link in links_with_operstate:
1239 self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
e2aea43f 1240
53c32c2b 1241 def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
7ef26afc 1242 for _ in range(timeout_sec * 2):
371810d1 1243 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
571f9539 1244 if re.search(address_regex, output) and 'tentative' not in output:
2629df47 1245 break
7ef26afc 1246 time.sleep(0.5)
240e4137
YW
1247
1248 self.assertRegex(output, address_regex)
1249
1250 def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
7ef26afc 1251 for _ in range(timeout_sec * 2):
240e4137
YW
1252 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1253 if not re.search(address_regex, output):
1254 break
7ef26afc 1255 time.sleep(0.5)
240e4137
YW
1256
1257 self.assertNotRegex(output, address_regex)
2629df47 1258
bb80f633 1259 def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
7ef26afc 1260 for _ in range(timeout_sec * 2):
bb80f633
YW
1261 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1262 if re.search(route_regex, output):
1263 break
7ef26afc 1264 time.sleep(0.5)
bb80f633
YW
1265
1266 self.assertRegex(output, route_regex)
1267
9dcdf16b 1268 def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
7ef26afc 1269 for _ in range(timeout_sec * 2):
9dcdf16b
YW
1270 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1271 if not re.search(route_regex, output):
1272 break
7ef26afc 1273 time.sleep(0.5)
9dcdf16b
YW
1274
1275 self.assertNotRegex(output, route_regex)
1276
a4640bed
TM
1277 def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
1278 if not shutil.which('selinuxenabled'):
e4377659 1279 print('## Checking NetLabel skipped: selinuxenabled command not found.')
a4640bed 1280 elif call_quiet('selinuxenabled') != 0:
e4377659 1281 print('## Checking NetLabel skipped: SELinux disabled.')
a4640bed 1282 elif not shutil.which('netlabelctl'): # not packaged by all distros
e4377659 1283 print('## Checking NetLabel skipped: netlabelctl command not found.')
a4640bed
TM
1284 else:
1285 output = check_output('netlabelctl unlbl list')
1286 print(output)
1287 self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
1288
c742d7e8
TM
1289 def setup_nftset(self, filter_name, filter_type, flags=''):
1290 if not shutil.which('nft'):
1291 print('## Setting up NFT sets skipped: nft command not found.')
1292 else:
1293 if call(f'nft add table inet sd_test') != 0:
1294 print('## Setting up NFT table failed.')
1295 self.fail()
1296 if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1297 print('## Setting up NFT sets failed.')
1298 self.fail()
1299
1300 def teardown_nftset(self, *filters):
1301 if not shutil.which('nft'):
1302 print('## Tearing down NFT sets skipped: nft command not found.')
1303 else:
1304 for filter_name in filters:
1305 if call(f'nft delete set inet sd_test {filter_name}') != 0:
1306 print('## Tearing down NFT sets failed.')
1307 self.fail()
1308 if call(f'nft delete table inet sd_test') != 0:
1309 print('## Tearing down NFT table failed.')
1310 self.fail()
1311
1312 def check_nftset(self, filter_name, contents):
1313 if not shutil.which('nft'):
1314 print('## Checking NFT sets skipped: nft command not found.')
1315 else:
1316 output = check_output(f'nft list set inet sd_test {filter_name}')
1317 print(output)
1318 self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
1319
45c2bbba
YW
1320 def check_networkd_log(self, contents, since=None, trial=20):
1321 for _ in range(trial):
1322 if contents in read_networkd_log(since=since):
1323 break
1324 time.sleep(0.5)
1325 else:
1326 self.fail(f'"{contents}" not found in journal.')
1327
8f28d342
YW
1328 def networkctl_check_unit(self, ifname, netdev_file=None, network_file=None, link_file=None):
1329 output = networkctl_status(ifname)
1330 print(output)
1331 if netdev_file:
1332 self.assertRegex(output, rf'NetDev File: .*/{netdev_file}\.netdev')
1333 else:
1334 self.assertNotIn('NetDev File:', output)
1335 if network_file:
1336 self.assertRegex(output, rf'Network File: .*/{network_file}\.network')
1337 else:
1338 self.assertIn('Network File: n/a', output)
1339 if link_file:
1340 self.assertRegex(output, rf'Link File: .*/{link_file}\.link')
1341
1ca44d7d
YW
1342class NetworkctlTests(unittest.TestCase, Utilities):
1343
1ca44d7d 1344 def setUp(self):
a962d857 1345 setup_common()
1ca44d7d
YW
1346
1347 def tearDown(self):
a962d857 1348 tear_down_common()
1ca44d7d 1349
6934ace0
YW
1350 @expectedFailureIfAlternativeNameIsNotAvailable()
1351 def test_altname(self):
a962d857 1352 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
6934ace0 1353 start_networkd()
95e1fbba 1354 self.wait_online('dummy98:degraded')
6934ace0 1355
10d670a3 1356 output = networkctl_status('dummy98')
6934ace0
YW
1357 self.assertRegex(output, 'hogehogehogehogehogehoge')
1358
f68f644a
NR
1359 @expectedFailureIfAlternativeNameIsNotAvailable()
1360 def test_rename_to_altname(self):
1361 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1362 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1363 start_networkd()
95e1fbba 1364 self.wait_online('dummyalt:degraded')
f68f644a 1365
10d670a3 1366 output = networkctl_status('dummyalt')
f68f644a
NR
1367 self.assertIn('hogehogehogehogehogehoge', output)
1368 self.assertNotIn('dummy98', output)
1369
dcd9f07c 1370 def test_reconfigure(self):
67150a7b 1371 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
dcd9f07c 1372 start_networkd()
95e1fbba 1373 self.wait_online('dummy98:routable')
dcd9f07c
YW
1374
1375 output = check_output('ip -4 address show dev dummy98')
1376 print(output)
3bad5487
YW
1377 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1378 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1379 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
dcd9f07c
YW
1380
1381 check_output('ip address del 10.1.2.3/16 dev dummy98')
1382 check_output('ip address del 10.1.2.4/16 dev dummy98')
1383 check_output('ip address del 10.2.2.4/16 dev dummy98')
1384
a962d857 1385 networkctl_reconfigure('dummy98')
95e1fbba 1386 self.wait_online('dummy98:routable')
dcd9f07c
YW
1387
1388 output = check_output('ip -4 address show dev dummy98')
1389 print(output)
3bad5487
YW
1390 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1391 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1392 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1393
a962d857 1394 remove_network_unit('25-address-static.network')
3bad5487 1395
a962d857 1396 networkctl_reload()
3bad5487
YW
1397 self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
1398
1399 output = check_output('ip -4 address show dev dummy98')
1400 print(output)
1401 self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1402 self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1403 self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1404
67150a7b 1405 copy_network_unit('25-address-static.network', copy_dropins=False)
a962d857 1406 networkctl_reload()
95e1fbba 1407 self.wait_online('dummy98:routable')
3bad5487
YW
1408
1409 output = check_output('ip -4 address show dev dummy98')
1410 print(output)
1411 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1412 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1413 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
dcd9f07c 1414
7e107bc3
FS
1415 def test_renew(self):
1416 def check():
95e1fbba 1417 self.wait_online('veth99:routable', 'veth-peer:routable')
10d670a3 1418 output = networkctl_status('veth99')
7e107bc3 1419 print(output)
e6c4b5dc 1420 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7e107bc3
FS
1421 self.assertIn('Gateway: 192.168.5.3', output)
1422 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
1423 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
1424
1425 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1426 start_networkd()
1427 check()
10d670a3 1428 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
7e107bc3
FS
1429
1430 for verb in ['renew', 'forcerenew']:
10d670a3 1431 networkctl(verb, 'veth99')
7e107bc3 1432 check()
10d670a3 1433 networkctl(verb, 'veth99', 'veth99', 'veth99')
7e107bc3
FS
1434 check()
1435
1436 def test_up_down(self):
67150a7b 1437 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
7e107bc3 1438 start_networkd()
95e1fbba 1439 self.wait_online('dummy98:routable')
7e107bc3 1440
10d670a3 1441 networkctl('down', 'dummy98')
95e1fbba 1442 self.wait_online('dummy98:off')
10d670a3 1443 networkctl('up', 'dummy98')
95e1fbba 1444 self.wait_online('dummy98:routable')
10d670a3 1445 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
95e1fbba 1446 self.wait_online('dummy98:off')
10d670a3 1447 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
95e1fbba 1448 self.wait_online('dummy98:routable')
7e107bc3 1449
66de8671 1450 def test_reload(self):
a962d857 1451 start_networkd()
66de8671 1452
a962d857
YW
1453 copy_network_unit('11-dummy.netdev')
1454 networkctl_reload()
19cf3143 1455 self.wait_operstate('test1', 'off', setup_state='unmanaged')
66de8671 1456
a962d857
YW
1457 copy_network_unit('11-dummy.network')
1458 networkctl_reload()
95e1fbba 1459 self.wait_online('test1:degraded')
66de8671 1460
a962d857
YW
1461 remove_network_unit('11-dummy.network')
1462 networkctl_reload()
19cf3143 1463 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
66de8671 1464
a962d857
YW
1465 remove_network_unit('11-dummy.netdev')
1466 networkctl_reload()
19cf3143 1467 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
66de8671 1468
a962d857
YW
1469 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1470 networkctl_reload()
19cf3143 1471 self.wait_operstate('test1', 'degraded')
66de8671 1472
1ca44d7d 1473 def test_glob(self):
a962d857 1474 copy_network_unit('11-dummy.netdev', '11-dummy.network')
2cf6fdff 1475 start_networkd()
1ca44d7d 1476
95e1fbba 1477 self.wait_online('test1:degraded')
1ca44d7d 1478
10d670a3 1479 output = networkctl('list')
1ca44d7d
YW
1480 self.assertRegex(output, '1 lo ')
1481 self.assertRegex(output, 'test1')
1482
10d670a3 1483 output = networkctl('list', 'test1')
1ca44d7d
YW
1484 self.assertNotRegex(output, '1 lo ')
1485 self.assertRegex(output, 'test1')
1486
10d670a3 1487 output = networkctl('list', 'te*')
1ca44d7d
YW
1488 self.assertNotRegex(output, '1 lo ')
1489 self.assertRegex(output, 'test1')
1490
10d670a3 1491 output = networkctl_status('te*')
1ca44d7d
YW
1492 self.assertNotRegex(output, '1: lo ')
1493 self.assertRegex(output, 'test1')
1494
10d670a3 1495 output = networkctl_status('tes[a-z][0-9]')
1ca44d7d
YW
1496 self.assertNotRegex(output, '1: lo ')
1497 self.assertRegex(output, 'test1')
1498
6d5b4efe 1499 def test_mtu(self):
a962d857 1500 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
2cf6fdff 1501 start_networkd()
6d5b4efe 1502
95e1fbba 1503 self.wait_online('test1:degraded')
6d5b4efe 1504
10d670a3 1505 output = networkctl_status('test1')
6d5b4efe
YW
1506 self.assertRegex(output, 'MTU: 1600')
1507
e28fd95f 1508 def test_type(self):
a962d857 1509 copy_network_unit('11-dummy.netdev', '11-dummy.network')
e28fd95f 1510 start_networkd()
95e1fbba 1511 self.wait_online('test1:degraded')
e28fd95f 1512
10d670a3 1513 output = networkctl_status('test1')
e28fd95f
YW
1514 print(output)
1515 self.assertRegex(output, 'Type: ether')
1516
10d670a3 1517 output = networkctl_status('lo')
e28fd95f
YW
1518 print(output)
1519 self.assertRegex(output, 'Type: loopback')
1520
b993e7e7
YW
1521 def test_unit_file(self):
1522 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
e28fd95f 1523 start_networkd()
95e1fbba 1524 self.wait_online('test1:degraded')
e28fd95f 1525
10d670a3 1526 output = networkctl_status('test1')
e28fd95f 1527 print(output)
b993e7e7
YW
1528 self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
1529 self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
1530 self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
1531 self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
1532
45c2bbba 1533 self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).')
e28fd95f 1534
df0a741c
YW
1535 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1536 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1537 # Let's reprocess the interface and drop the property.
a39a2a81 1538 udevadm_trigger('/sys/class/net/lo')
10d670a3 1539 output = networkctl_status('lo')
e28fd95f 1540 print(output)
b993e7e7
YW
1541 self.assertIn('Link File: n/a', output)
1542 self.assertIn('Network File: n/a', output)
e28fd95f 1543
bee692fd 1544 def test_delete_links(self):
acc06d8a 1545 copy_network_unit('11-dummy.netdev', '25-veth.netdev')
2cf6fdff 1546 start_networkd()
acc06d8a 1547 self.wait_links('test1', 'veth99', 'veth-peer')
10d670a3 1548 networkctl('delete', 'test1', 'veth99')
acc06d8a 1549 self.check_link_exists('test1', 'veth99', 'veth-peer', expected=False)
bee692fd 1550
7e107bc3 1551 def test_label(self):
10d670a3 1552 networkctl('label')
7e107bc3 1553
fa4d3fed
YW
1554class NetworkdMatchTests(unittest.TestCase, Utilities):
1555
1556 def setUp(self):
1557 setup_common()
1558
1559 def tearDown(self):
1560 tear_down_common()
1561
7618ab1b 1562 @expectedFailureIfAlternativeNameIsNotAvailable()
fa4d3fed
YW
1563 def test_match(self):
1564 copy_network_unit('12-dummy-mac.netdev',
1565 '12-dummy-match-mac-01.network',
1566 '12-dummy-match-mac-02.network',
1567 '12-dummy-match-renamed.network',
1568 '12-dummy-match-altname.network',
1569 '12-dummy-altname.link')
1570 start_networkd()
1571
95e1fbba 1572 self.wait_online('dummy98:routable')
10d670a3 1573 output = networkctl_status('dummy98')
fa4d3fed
YW
1574 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
1575 output = check_output('ip -4 address show dev dummy98')
1576 self.assertIn('10.0.0.1/16', output)
1577
1578 check_output('ip link set dev dummy98 down')
1579 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1580
1581 self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1582 self.wait_online('dummy98:routable')
10d670a3 1583 output = networkctl_status('dummy98')
fa4d3fed
YW
1584 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
1585
1586 check_output('ip link set dev dummy98 down')
1587 check_output('ip link set dev dummy98 name dummy98-1')
1588
1589 self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1590 self.wait_online('dummy98-1:routable')
10d670a3 1591 output = networkctl_status('dummy98-1')
fa4d3fed
YW
1592 self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
1593
1594 check_output('ip link set dev dummy98-1 down')
1595 check_output('ip link set dev dummy98-1 name dummy98-2')
a39a2a81 1596 udevadm_trigger('/sys/class/net/dummy98-2')
fa4d3fed
YW
1597
1598 self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
95e1fbba 1599 self.wait_online('dummy98-2:routable')
10d670a3 1600 output = networkctl_status('dummy98-2')
fa4d3fed
YW
1601 self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
1602
5432adae
YW
1603 def test_match_udev_property(self):
1604 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1605 start_networkd()
95e1fbba 1606 self.wait_online('dummy98:routable')
5432adae 1607
10d670a3 1608 output = networkctl_status('dummy98')
5432adae
YW
1609 print(output)
1610 self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
1611
b09ec847
YW
1612class WaitOnlineTests(unittest.TestCase, Utilities):
1613
1614 def setUp(self):
1615 setup_common()
1616
1617 def tearDown(self):
1618 tear_down_common()
1619
1620 def test_wait_online_any(self):
1621 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1622 start_networkd()
1623
95e1fbba 1624 self.wait_online('bridge99', 'test1:degraded', bool_any=True)
b09ec847
YW
1625
1626 self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
1627 self.wait_operstate('test1', 'degraded')
1628
d6608da1
NR
1629 def do_test_wait_online_dns(
1630 self,
1631 global_dns='',
1632 fallback_dns='',
1633 expect_timeout=False,
1634 network_dropin=None,
1635 ):
1636 global wait_online_env
1637
1638 if network_dropin is not None:
1639 network_dropin_path = os.path.join(
1640 network_unit_dir,
1641 '25-dhcp-client-use-dns-ipv4.network.d/test.conf'
1642 )
1643 mkdir_p(os.path.dirname(network_dropin_path))
1644 with open(network_dropin_path, 'w') as f:
1645 f.write(network_dropin)
1646
1647 copy_network_unit(
1648 '25-veth.netdev',
1649 '25-dhcp-client-use-dns-ipv4.network',
1650 '25-dhcp-server.network'
1651 )
1652 start_networkd()
1653 self.wait_online('veth-peer:routable')
1654
1655 # Unless given, clear global DNS configuration
1656 resolved_dropin = '/run/systemd/resolved.conf.d/global-dns.conf'
1657 mkdir_p(os.path.dirname(resolved_dropin))
1658 with open(resolved_dropin, 'w') as f:
1659 f.write((
1660 '[Resolve]\n'
1661 f'DNS={global_dns}\n'
1662 f'FallbackDNS={fallback_dns}\n'
1663 ))
1664 self.addCleanup(os.remove, resolved_dropin)
1665 check_output('systemctl reload systemd-resolved')
1666
1667 try:
1668 wait_online_env_copy = wait_online_env.copy()
1669
1670 wait_online_env['SYSTEMD_LOG_LEVEL'] = 'debug'
1671 wait_online_env['SYSTEMD_LOG_TARGET'] = 'console'
1672
1673 self.wait_online('veth99:routable', bool_dns=True)
1674
1675 if expect_timeout:
1676 # The above should have thrown an exception.
1677 self.fail(
1678 'Expected systemd-networkd-wait-online to time out'
1679 )
1680
1681 except subprocess.CalledProcessError as e:
1682 if expect_timeout:
1683 self.assertRegex(
1684 e.output,
1685 f'veth99: No link-specific DNS server is accessible',
1686 f'Missing expected log message:\n{e.output}'
1687 )
1688 else:
1689 self.fail(
1690 f'Command timed out:\n{e.output}'
1691 )
1692 finally:
1693 wait_online_env = wait_online_env_copy
1694
1695 def test_wait_online_dns(self):
1696 ''' test systemd-networkd-wait-online with --dns '''
1697 self.do_test_wait_online_dns()
1698
1699 def test_wait_online_dns_global(self):
1700 '''
1701 test systemd-networkd-wait-online with --dns, expect pass due to global DNS
1702 '''
1703
1704 # Set UseDNS=no, and allow global DNS to be used.
1705 self.do_test_wait_online_dns(
1706 global_dns='192.168.5.1',
1707 network_dropin=(
1708 '[DHCPv4]\n'
1709 'UseDNS=no\n'
1710 )
1711 )
1712
1713 def test_wait_online_dns_expect_timeout(self):
1714 ''' test systemd-networkd-wait-online with --dns, and expect timeout '''
1715
1716 # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
1717 self.do_test_wait_online_dns(
1718 expect_timeout=True,
1719 network_dropin=(
1720 '[Network]\n'
1721 'DNSDefaultRoute=yes\n'
1722 '[DHCPv4]\n'
1723 'UseDNS=no\n'
1724 )
1725 )
1726
1727 def test_wait_online_dns_expect_timeout_global(self):
1728 '''
1729 test systemd-networkd-wait-online with --dns, and expect timeout
1730 despite global DNS
1731 '''
1732
1733 # Configure Domains=~., and expect timeout despite global DNS servers
1734 # being available.
1735 self.do_test_wait_online_dns(
1736 expect_timeout=True,
1737 global_dns='192.168.5.1',
1738 network_dropin=(
1739 '[Network]\n'
1740 'Domains=~.\n'
1741 '[DHCPv4]\n'
1742 'UseDNS=no\n'
1743 )
1744 )
1745
1746
1f0e3109
SS
1747class NetworkdNetDevTests(unittest.TestCase, Utilities):
1748
1f0e3109 1749 def setUp(self):
a962d857 1750 setup_common()
1f0e3109
SS
1751
1752 def tearDown(self):
a962d857 1753 tear_down_common()
1f0e3109 1754
1ca44d7d 1755 def test_dropin_and_name_conflict(self):
a962d857 1756 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
2cf6fdff 1757 start_networkd()
d80734f7 1758
95e1fbba 1759 self.wait_online('dropin-test:off', setup_state='unmanaged')
d80734f7 1760
371810d1 1761 output = check_output('ip link show dropin-test')
d80734f7 1762 print(output)
45aa0e84 1763 self.assertRegex(output, '00:50:56:c0:00:28')
d80734f7 1764
13060471
YW
1765 @expectedFailureIfModuleIsNotAvailable('bareudp')
1766 def test_bareudp(self):
a962d857 1767 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
13060471
YW
1768 start_networkd()
1769
95e1fbba 1770 self.wait_online('bareudp99:degraded')
8f28d342 1771 self.networkctl_check_unit('bareudp99', '25-bareudp', '26-netdev-link-local-addressing-yes')
13060471
YW
1772
1773 output = check_output('ip -d link show bareudp99')
1774 print(output)
1775 self.assertRegex(output, 'dstport 1000 ')
1776 self.assertRegex(output, 'ethertype ip ')
3859ef16 1777 self.assertRegex(output, 'srcportmin 1001 ')
13060471 1778
5b73edfa
YW
1779 touch_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1780 networkctl_reload()
1781 self.wait_online('bareudp99:degraded')
1782
c0267a59
AW
1783 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1784 def test_batadv(self):
a962d857 1785 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
c0267a59
AW
1786 start_networkd()
1787
95e1fbba 1788 self.wait_online('batadv99:degraded')
8f28d342 1789 self.networkctl_check_unit('batadv99', '25-batadv', '26-netdev-link-local-addressing-yes')
c0267a59
AW
1790
1791 output = check_output('ip -d link show batadv99')
1792 print(output)
1793 self.assertRegex(output, 'batadv')
1794
5b73edfa
YW
1795 touch_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1796 networkctl_reload()
1797 self.wait_online('batadv99:degraded')
1798
1f0e3109 1799 def test_bridge(self):
a962d857 1800 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
2cf6fdff 1801 start_networkd()
1f0e3109 1802
95e1fbba 1803 self.wait_online('bridge99:no-carrier')
8f28d342 1804 self.networkctl_check_unit('bridge99', '25-bridge', '25-bridge-configure-without-carrier')
1f0e3109 1805
3d165124 1806 tick = os.sysconf('SC_CLK_TCK')
ec38833c
ZJS
1807 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
1808 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
c9047092
YW
1809 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick))
1810 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick))
1811 self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1812 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1813 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1814 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1815 self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
d7de242c 1816 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn')))
1f0e3109 1817
10d670a3 1818 output = networkctl_status('bridge99')
36bc2ffb
YW
1819 print(output)
1820 self.assertRegex(output, 'Priority: 9')
1821 self.assertRegex(output, 'STP: yes')
1822 self.assertRegex(output, 'Multicast IGMP Version: 3')
1aa74ad0
GH
1823 if 'FDB Max Learned' in output:
1824 self.assertRegex(output, 'FDB Max Learned: 4')
36bc2ffb 1825
b6d5dab7
YW
1826 output = check_output('ip -d link show bridge99')
1827 print(output)
1828 self.assertIn('vlan_filtering 1 ', output)
1829 self.assertIn('vlan_protocol 802.1ad ', output)
1830 self.assertIn('vlan_default_pvid 9 ', output)
1aa74ad0
GH
1831 if 'fdb_max_learned' in output:
1832 self.assertIn('fdb_max_learned 4 ', output)
b6d5dab7 1833
1f0e3109 1834 def test_bond(self):
d2d0a8d4 1835 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
2cf6fdff 1836 start_networkd()
ec38833c 1837
d2d0a8d4 1838 self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged')
8f28d342
YW
1839 self.networkctl_check_unit('bond99', '25-bond')
1840 self.networkctl_check_unit('bond98', '25-bond-balanced-tlb')
1841 self.networkctl_check_unit('bond97', '25-bond-property')
ec38833c 1842
a962d857
YW
1843 self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1844 self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1845 self.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1846 self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1847 self.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1848 self.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1849 self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1850 self.check_link_attr('bond99', 'bonding', 'min_links', '1')
1851 self.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1852 self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1853 self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1854
1855 self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1856 self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
fde60a42 1857
10d670a3 1858 output = networkctl_status('bond99')
acfe3b65
YW
1859 print(output)
1860 self.assertIn('Mode: 802.3ad', output)
1861 self.assertIn('Miimon: 1s', output)
1862 self.assertIn('Updelay: 2s', output)
1863 self.assertIn('Downdelay: 2s', output)
1864
10d670a3 1865 output = networkctl_status('bond98')
acfe3b65
YW
1866 print(output)
1867 self.assertIn('Mode: balance-tlb', output)
1868
d2d0a8d4
SS
1869 output = networkctl_status('bond97')
1870 print(output)
1871
1872 self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
de736b96 1873 self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
d2d0a8d4 1874
0de55624 1875 def check_vlan(self, id, flags):
95e1fbba 1876 self.wait_online('test1:degraded', 'vlan99:routable')
8f28d342
YW
1877 self.networkctl_check_unit('vlan99', '21-vlan', '21-vlan')
1878 self.networkctl_check_unit('test1', '11-dummy', '21-vlan-test1')
1f0e3109 1879
371810d1 1880 output = check_output('ip -d link show test1')
72b7f1b9 1881 print(output)
7d7be1b9 1882 self.assertRegex(output, ' mtu 2000 ')
72b7f1b9 1883
371810d1 1884 output = check_output('ip -d link show vlan99')
14ecd604 1885 print(output)
73d24e45 1886 self.assertIn(' mtu 2000 ', output)
0de55624
YW
1887 if flags:
1888 self.assertIn('REORDER_HDR', output)
1889 self.assertIn('LOOSE_BINDING', output)
1890 self.assertIn('GVRP', output)
1891 self.assertIn('MVRP', output)
1892 else:
1893 self.assertNotIn('REORDER_HDR', output)
1894 self.assertNotIn('LOOSE_BINDING', output)
1895 self.assertNotIn('GVRP', output)
1896 self.assertNotIn('MVRP', output)
1897 self.assertIn(f' id {id} ', output)
73d24e45
YW
1898 self.assertIn('ingress-qos-map { 4:100 7:13 }', output)
1899 self.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output)
1f0e3109 1900
371810d1 1901 output = check_output('ip -4 address show dev test1')
7f45d738
YW
1902 print(output)
1903 self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1904 self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1905
371810d1 1906 output = check_output('ip -4 address show dev vlan99')
7f45d738
YW
1907 print(output)
1908 self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1909
0de55624
YW
1910 def test_vlan(self):
1911 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1912 '21-vlan.network', '21-vlan-test1.network')
1913 start_networkd()
1914 self.check_vlan(id=99, flags=True)
1915
1916 # Test for reloading .netdev file. See issue #34907.
1917 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1918 f.write('[VLAN]\nId=42\n')
1919
1920 # VLAN ID cannot be changed, so we need to remove the existing netdev.
1921 check_output("ip link del vlan99")
1922 networkctl_reload()
1923 self.check_vlan(id=42, flags=True)
1924
1925 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1926 f.write('[VLAN]\n'
1927 'GVRP=no\n'
1928 'MVRP=no\n'
1929 'LooseBinding=no\n'
1930 'ReorderHeader=no\n')
1931
1932 # flags can be changed, hence it is not necessary to remove the existing netdev.
1933 networkctl_reload()
1934 self.check_vlan(id=42, flags=False)
1935
b249834b
YW
1936 def test_vlan_on_bond(self):
1937 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1938 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1939
1940 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1941 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1942 start_networkd()
95e1fbba 1943 self.wait_online('bond99:off')
b249834b 1944 self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
8f28d342
YW
1945 self.networkctl_check_unit('vlan99', '21-vlan-on-bond', '21-vlan-on-bond')
1946 self.networkctl_check_unit('bond99', '21-bond-802.3ad', '21-bond-802.3ad')
b249834b 1947
45c2bbba 1948 self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
b249834b
YW
1949
1950 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1951 networkctl_reload()
95e1fbba 1952 self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
b249834b 1953
1f0e3109 1954 def test_macvtap(self):
a962d857 1955 first = True
460feb61 1956 for mode in ['private', 'vepa', 'bridge', 'passthru']:
a962d857
YW
1957 if first:
1958 first = False
1959 else:
1960 self.tearDown()
1961
1962 print(f'### test_macvtap(mode={mode})')
460feb61 1963 with self.subTest(mode=mode):
a962d857
YW
1964 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1965 '11-dummy.netdev', '25-macvtap.network')
1966 with open(os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8') as f:
460feb61 1967 f.write('[MACVTAP]\nMode=' + mode)
2cf6fdff 1968 start_networkd()
1f0e3109 1969
95e1fbba
YW
1970 self.wait_online('macvtap99:degraded',
1971 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
8f28d342
YW
1972 self.networkctl_check_unit('macvtap99', '21-macvtap', '26-netdev-link-local-addressing-yes')
1973 self.networkctl_check_unit('test1', '11-dummy', '25-macvtap')
460feb61 1974
371810d1 1975 output = check_output('ip -d link show macvtap99')
460feb61
YW
1976 print(output)
1977 self.assertRegex(output, 'macvtap mode ' + mode + ' ')
1f0e3109 1978
0de55624
YW
1979 touch_network_unit('21-macvtap.netdev')
1980 networkctl_reload()
1981 self.wait_online('macvtap99:degraded',
1982 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1983
e97bb361 1984 @expectedFailureIfModuleIsNotAvailable('macvlan')
1f0e3109 1985 def test_macvlan(self):
a962d857 1986 first = True
dff9792b 1987 for mode in ['private', 'vepa', 'bridge', 'passthru']:
a962d857
YW
1988 if first:
1989 first = False
1990 else:
1991 self.tearDown()
1992
1993 print(f'### test_macvlan(mode={mode})')
dff9792b 1994 with self.subTest(mode=mode):
a962d857
YW
1995 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1996 '11-dummy.netdev', '25-macvlan.network')
1997 with open(os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8') as f:
dff9792b 1998 f.write('[MACVLAN]\nMode=' + mode)
2cf6fdff 1999 start_networkd()
dff9792b 2000
95e1fbba
YW
2001 self.wait_online('macvlan99:degraded',
2002 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
8f28d342
YW
2003 self.networkctl_check_unit('macvlan99', '21-macvlan', '26-netdev-link-local-addressing-yes')
2004 self.networkctl_check_unit('test1', '11-dummy', '25-macvlan')
dff9792b 2005
371810d1 2006 output = check_output('ip -d link show test1')
dff9792b 2007 print(output)
0183d48d 2008 self.assertIn(' mtu 2000 ', output)
72b7f1b9 2009
371810d1 2010 output = check_output('ip -d link show macvlan99')
dff9792b 2011 print(output)
0183d48d
YW
2012 self.assertIn(' mtu 2000 ', output)
2013 self.assertIn(f' macvlan mode {mode} ', output)
72b7f1b9 2014
a962d857 2015 remove_link('test1')
1d0c9bd7
YW
2016 time.sleep(1)
2017
a962d857 2018 check_output("ip link add test1 type dummy")
95e1fbba
YW
2019 self.wait_online('macvlan99:degraded',
2020 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1d0c9bd7
YW
2021
2022 output = check_output('ip -d link show test1')
2023 print(output)
0183d48d 2024 self.assertIn(' mtu 2000 ', output)
1d0c9bd7
YW
2025
2026 output = check_output('ip -d link show macvlan99')
2027 print(output)
0183d48d
YW
2028 self.assertIn(' mtu 2000 ', output)
2029 self.assertIn(f' macvlan mode {mode} ', output)
2b98febe 2030 self.assertIn(' bcqueuelen 1234 ', output)
2a4f9139
YW
2031 if ' bclim ' in output: # This is new in kernel and iproute2 v6.4
2032 self.assertIn(' bclim 2147483647 ', output)
1d0c9bd7 2033
0de55624
YW
2034 touch_network_unit('21-macvlan.netdev')
2035 networkctl_reload()
2036 self.wait_online('macvlan99:degraded',
2037 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2038
7a0a37b2 2039 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1f0e3109 2040 def test_ipvlan(self):
a962d857 2041 first = True
bc6dff6e 2042 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
a962d857
YW
2043 if first:
2044 first = False
2045 else:
2046 self.tearDown()
2047
2048 print(f'### test_ipvlan(mode={mode}, flag={flag})')
bc6dff6e 2049 with self.subTest(mode=mode, flag=flag):
a962d857
YW
2050 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
2051 '11-dummy.netdev', '25-ipvlan.network')
2052 with open(os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8') as f:
bc6dff6e 2053 f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
1f0e3109 2054
2cf6fdff 2055 start_networkd()
95e1fbba 2056 self.wait_online('ipvlan99:degraded', 'test1:degraded')
8f28d342
YW
2057 self.networkctl_check_unit('ipvlan99', '25-ipvlan', '26-netdev-link-local-addressing-yes')
2058 self.networkctl_check_unit('test1', '11-dummy', '25-ipvlan')
bc6dff6e 2059
371810d1 2060 output = check_output('ip -d link show ipvlan99')
bc6dff6e
YW
2061 print(output)
2062 self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
1f0e3109 2063
0de55624
YW
2064 touch_network_unit('25-ipvlan.netdev')
2065 networkctl_reload()
2066 self.wait_online('ipvlan99:degraded', 'test1:degraded')
2067
f7996e2a
BG
2068 @expectedFailureIfModuleIsNotAvailable('hsr')
2069 def test_hsr(self):
2070 first = True
2071 for proto, supervision in [['hsr', 9], ['prp', 127]]:
2072 if first:
2073 first = False
2074 else:
2075 self.tearDown()
2076
2077 print(f'### test_hsr(proto={proto}, supervision={supervision})')
2078 with self.subTest(proto=proto, supervision=supervision):
2079 copy_network_unit('25-hsr.netdev', '25-hsr.network',
2080 '11-dummy.netdev', '11-dummy.network',
2081 '12-dummy.netdev', '12-dummy-no-address.network')
2082 with open(os.path.join(network_unit_dir, '25-hsr.netdev'), mode='a', encoding='utf-8') as f:
2083 f.write('Protocol=' + proto + '\nSupervision=' + str(supervision))
2084
2085 start_networkd()
2086 self.wait_online('hsr99:degraded')
2087 self.networkctl_check_unit('hsr99', '25-hsr', '25-hsr')
2088 self.networkctl_check_unit('test1', '11-dummy', '11-dummy')
2089 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy-no-address')
2090
2091 output = check_output('ip -d link show hsr99')
2092 print(output)
2093 self.assertRegex(output, 'hsr slave1 test1 slave2 dummy98')
2094 self.assertRegex(output, f'supervision 01:15:4e:00:01:{supervision:02x}')
2095 self.assertRegex(output, 'proto ' + ('0' if proto == 'hsr' else '1') + ' ')
2096
2097 touch_network_unit('25-hsr.netdev')
2098 networkctl_reload()
2099 self.wait_online('hsr99:degraded')
2100
956c8fec
YW
2101 @expectedFailureIfModuleIsNotAvailable('ipvtap')
2102 def test_ipvtap(self):
a962d857 2103 first = True
40921f08 2104 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
a962d857
YW
2105 if first:
2106 first = False
2107 else:
2108 self.tearDown()
2109
2110 print(f'### test_ipvtap(mode={mode}, flag={flag})')
40921f08 2111 with self.subTest(mode=mode, flag=flag):
a962d857
YW
2112 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
2113 '11-dummy.netdev', '25-ipvtap.network')
2114 with open(os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8') as f:
40921f08
YW
2115 f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
2116
2cf6fdff 2117 start_networkd()
95e1fbba 2118 self.wait_online('ipvtap99:degraded', 'test1:degraded')
8f28d342
YW
2119 self.networkctl_check_unit('ipvtap99', '25-ipvtap', '26-netdev-link-local-addressing-yes')
2120 self.networkctl_check_unit('test1', '11-dummy', '25-ipvtap')
956c8fec 2121
371810d1 2122 output = check_output('ip -d link show ipvtap99')
40921f08
YW
2123 print(output)
2124 self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
956c8fec 2125
0de55624
YW
2126 touch_network_unit('25-ipvtap.netdev')
2127 networkctl_reload()
2128 self.wait_online('ipvtap99:degraded', 'test1:degraded')
2129
1f0e3109 2130 def test_veth(self):
a962d857
YW
2131 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
2132 '25-veth-mtu.netdev')
2cf6fdff 2133 start_networkd()
1f0e3109 2134
95e1fbba 2135 self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
8f28d342
YW
2136 self.networkctl_check_unit('veth99', '25-veth', '26-netdev-link-local-addressing-yes')
2137 self.networkctl_check_unit('veth-peer', '25-veth', '26-netdev-link-local-addressing-yes')
2138 self.networkctl_check_unit('veth-mtu', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
2139 self.networkctl_check_unit('veth-mtu-peer', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
671dacdf 2140
371810d1 2141 output = check_output('ip -d link show veth99')
671dacdf
YW
2142 print(output)
2143 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
371810d1 2144 output = check_output('ip -d link show veth-peer')
671dacdf
YW
2145 print(output)
2146 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
1f0e3109 2147
0874be35
YW
2148 output = check_output('ip -d link show veth-mtu')
2149 print(output)
2150 self.assertRegex(output, 'link/ether 12:34:56:78:9a:be')
2151 self.assertRegex(output, 'mtu 1800')
2152 output = check_output('ip -d link show veth-mtu-peer')
2153 print(output)
2154 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
2155 self.assertRegex(output, 'mtu 1800')
2156
5b73edfa
YW
2157 touch_network_unit(
2158 '25-veth.netdev',
2159 '26-netdev-link-local-addressing-yes.network',
2160 '25-veth-mtu.netdev')
2161 networkctl_reload()
2162 self.wait_online(
2163 'veth99:degraded',
2164 'veth-peer:degraded',
2165 'veth-mtu:degraded',
2166 'veth-mtu-peer:degraded')
2167
acddb3cb 2168 def check_tuntap(self, attached):
ae014ecb
YW
2169 pid = networkd_pid()
2170 name = psutil.Process(pid).name()[:15]
2171
acddb3cb 2172 output = check_output('ip -d -oneline tuntap show')
ae014ecb 2173 print(output)
acddb3cb
YW
2174 self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
2175 self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
1f0e3109 2176
acddb3cb
YW
2177 if attached:
2178 self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)')
2179 self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)')
2180 self.assertRegex(output, r'testtap99: .*systemd\(1\)')
2181 self.assertRegex(output, r'testtun99: .*systemd\(1\)')
2746d307 2182
acddb3cb
YW
2183 output = check_output('ip -d link show testtun99')
2184 print(output)
2185 # Old ip command does not support IFF_ flags
2186 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2187 self.assertIn('UP,LOWER_UP', output)
ae014ecb 2188
acddb3cb
YW
2189 output = check_output('ip -d link show testtap99')
2190 print(output)
2191 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2192 self.assertIn('UP,LOWER_UP', output)
ae014ecb 2193
acddb3cb
YW
2194 else:
2195 self.assertNotIn(f'{name}({pid})', output)
2196 self.assertNotIn('systemd(1)', output)
ae014ecb 2197
acddb3cb
YW
2198 for _ in range(20):
2199 output = check_output('ip -d link show testtun99')
2200 print(output)
2201 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2202 if 'NO-CARRIER' in output:
2203 break
2204 time.sleep(0.5)
2205 else:
2206 self.fail()
ae014ecb 2207
acddb3cb
YW
2208 for _ in range(20):
2209 output = check_output('ip -d link show testtap99')
2210 print(output)
2211 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2212 if 'NO-CARRIER' in output:
2213 break
2214 time.sleep(0.5)
2215 else:
2216 self.fail()
ae014ecb 2217
acddb3cb
YW
2218 def test_tuntap(self):
2219 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2220 start_networkd()
2221 self.wait_online('testtun99:degraded', 'testtap99:degraded')
8f28d342
YW
2222 self.networkctl_check_unit('testtap99', '25-tap', '26-netdev-link-local-addressing-yes')
2223 self.networkctl_check_unit('testtun99', '25-tun', '26-netdev-link-local-addressing-yes')
ae014ecb 2224
acddb3cb 2225 self.check_tuntap(True)
ae014ecb 2226
5b73edfa
YW
2227 touch_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2228 networkctl_reload()
2229 self.wait_online('testtun99:degraded', 'testtap99:degraded')
2230
2231 self.check_tuntap(True)
2232
acddb3cb 2233 remove_network_unit('26-netdev-link-local-addressing-yes.network')
ae014ecb 2234 restart_networkd()
acddb3cb 2235 self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged')
8f28d342
YW
2236 self.networkctl_check_unit('testtap99', '25-tap')
2237 self.networkctl_check_unit('testtun99', '25-tun')
ae014ecb 2238
acddb3cb 2239 self.check_tuntap(True)
ae014ecb 2240
acddb3cb 2241 clear_network_units()
dadf2bd4 2242 unmanage_existing_links()
acddb3cb
YW
2243 restart_networkd()
2244 self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged')
8f28d342
YW
2245 self.networkctl_check_unit('testtap99')
2246 self.networkctl_check_unit('testtun99')
ae014ecb 2247
acddb3cb 2248 self.check_tuntap(False)
2746d307 2249
7a0a37b2 2250 @expectedFailureIfModuleIsNotAvailable('vrf')
1f0e3109 2251 def test_vrf(self):
a962d857 2252 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2253 start_networkd()
1f0e3109 2254
95e1fbba 2255 self.wait_online('vrf99:carrier')
8f28d342 2256 self.networkctl_check_unit('vrf99', '25-vrf', '26-netdev-link-local-addressing-yes')
1f0e3109 2257
5b73edfa
YW
2258 touch_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2259 networkctl()
2260 self.wait_online('vrf99:carrier')
2261
7a0a37b2 2262 @expectedFailureIfModuleIsNotAvailable('vcan')
1f0e3109 2263 def test_vcan(self):
470a329d
YW
2264 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
2265 '25-vcan98.netdev', '25-vcan98.network')
2cf6fdff 2266 start_networkd()
1f0e3109 2267
95e1fbba 2268 self.wait_online('vcan99:carrier', 'vcan98:carrier')
7155ad95 2269 # For can devices, 'carrier' is the default required operational state.
95e1fbba 2270 self.wait_online('vcan99', 'vcan98')
8f28d342
YW
2271 self.networkctl_check_unit('vcan99', '25-vcan', '26-netdev-link-local-addressing-yes')
2272 self.networkctl_check_unit('vcan98', '25-vcan98', '25-vcan98')
470a329d
YW
2273
2274 # https://github.com/systemd/systemd/issues/30140
2275 output = check_output('ip -d link show vcan99')
2276 print(output)
2277 self.assertIn('mtu 16 ', output)
2278
2279 output = check_output('ip -d link show vcan98')
2280 print(output)
2281 self.assertIn('mtu 16 ', output)
1f0e3109 2282
5b73edfa
YW
2283 touch_network_unit(
2284 '25-vcan.netdev',
2285 '26-netdev-link-local-addressing-yes.network',
2286 '25-vcan98.netdev',
2287 '25-vcan98.network')
2288 networkctl_reload()
2289 self.wait_online('vcan99:carrier', 'vcan98:carrier')
2290
f63b14d3
YW
2291 @expectedFailureIfModuleIsNotAvailable('vxcan')
2292 def test_vxcan(self):
a962d857 2293 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2294 start_networkd()
f63b14d3 2295
95e1fbba 2296 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
7155ad95 2297 # For can devices, 'carrier' is the default required operational state.
95e1fbba 2298 self.wait_online('vxcan99', 'vxcan-peer')
8f28d342
YW
2299 self.networkctl_check_unit('vxcan99', '25-vxcan', '26-netdev-link-local-addressing-yes')
2300 self.networkctl_check_unit('vxcan-peer', '25-vxcan', '26-netdev-link-local-addressing-yes')
f63b14d3 2301
5b73edfa
YW
2302 touch_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2303 networkctl()
2304 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
2305
7a3bc5a8
EV
2306 @expectedFailureIfModuleIsNotAvailable('wireguard')
2307 def test_wireguard(self):
fa724cd5
MY
2308 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
2309 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
2310 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
2311
a962d857
YW
2312 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
2313 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
4bf1a2c3 2314 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
a962d857 2315 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2cf6fdff 2316 start_networkd()
95e1fbba 2317 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
8f28d342
YW
2318 self.networkctl_check_unit('wg99', '25-wireguard', '25-wireguard')
2319 self.networkctl_check_unit('wg98', '25-wireguard-23-peers', '25-wireguard-23-peers')
2320 self.networkctl_check_unit('wg97', '25-wireguard-no-peer', '25-wireguard-no-peer')
84d32bf5
YW
2321
2322 output = check_output('ip -4 address show dev wg99')
2323 print(output)
2324 self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
5a0bd90b 2325
e4e0b239
YW
2326 output = check_output('ip -4 address show dev wg99')
2327 print(output)
2328 self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
2329
2330 output = check_output('ip -6 address show dev wg99')
2331 print(output)
2332 self.assertIn('inet6 fe80::1/64 scope link', output)
2333
045db4fa
YW
2334 output = check_output('ip -4 address show dev wg98')
2335 print(output)
2336 self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
2337
2338 output = check_output('ip -6 address show dev wg98')
2339 print(output)
2340 self.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output)
2341
6387cac3
YW
2342 output = check_output('ip -4 route show dev wg99 table 1234')
2343 print(output)
4db8ccbb 2344 self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
6387cac3
YW
2345
2346 output = check_output('ip -6 route show dev wg99 table 1234')
2347 print(output)
2348 self.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output)
2349
2350 output = check_output('ip -6 route show dev wg98 table 1234')
2351 print(output)
2352 self.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output)
2353 self.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output)
2354 self.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output)
2355 self.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output)
2356 self.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output)
2357 self.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output)
2358 self.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output)
2359 self.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output)
2360 self.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output)
2361 self.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output)
2362 self.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output)
2363 self.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output)
2364 self.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output)
2365 self.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output)
2366 self.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output)
2367 self.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output)
2368 self.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output)
2369 self.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output)
2370 self.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output)
2371 self.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output)
2372 self.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output)
2373 self.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output)
2374 self.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output)
2375 self.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output)
2376 self.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output)
2377 self.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output)
2378 self.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output)
2379 self.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output)
2380 self.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output)
2381 self.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output)
2382 self.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output)
2383 self.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output)
2384 self.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output)
2385 self.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output)
2386 self.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output)
2387 self.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output)
2388 self.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output)
2389 self.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output)
2390 self.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output)
2391 self.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output)
2392 self.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output)
2393 self.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output)
2394 self.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output)
2395 self.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output)
2396 self.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output)
2397 self.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output)
2398
7a3bc5a8 2399 if shutil.which('wg'):
371810d1 2400 call('wg')
5a0bd90b 2401
371810d1 2402 output = check_output('wg show wg99 listen-port')
84d32bf5 2403 self.assertEqual(output, '51820')
371810d1 2404 output = check_output('wg show wg99 fwmark')
84d32bf5
YW
2405 self.assertEqual(output, '0x4d2')
2406 output = check_output('wg show wg99 private-key')
2407 self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
371810d1 2408 output = check_output('wg show wg99 allowed-ips')
84d32bf5
YW
2409 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
2410 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
2411 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
2412 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
371810d1 2413 output = check_output('wg show wg99 persistent-keepalive')
84d32bf5
YW
2414 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
2415 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
2416 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
2417 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
371810d1 2418 output = check_output('wg show wg99 endpoints')
84d32bf5
YW
2419 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
2420 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
2421 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
2422 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
371810d1 2423 output = check_output('wg show wg99 preshared-keys')
84d32bf5
YW
2424 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
2425 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
2426 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
2427 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
7a3bc5a8 2428
371810d1 2429 output = check_output('wg show wg98 private-key')
84d32bf5 2430 self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
da44fb8a 2431
da3509f0 2432 output = check_output('wg show wg97 listen-port')
84d32bf5 2433 self.assertEqual(output, '51821')
da3509f0 2434 output = check_output('wg show wg97 fwmark')
84d32bf5 2435 self.assertEqual(output, '0x4d3')
da3509f0 2436
5b73edfa
YW
2437 touch_network_unit(
2438 '25-wireguard.netdev', '25-wireguard.network',
2439 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
2440 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
2441 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2442 networkctl_reload()
2443 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
2444
1f0e3109 2445 def test_geneve(self):
a962d857 2446 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 2447 start_networkd()
1f0e3109 2448
95e1fbba 2449 self.wait_online('geneve99:degraded')
8f28d342 2450 self.networkctl_check_unit('geneve99', '25-geneve', '26-netdev-link-local-addressing-yes')
1f0e3109 2451
371810d1 2452 output = check_output('ip -d link show geneve99')
14ecd604 2453 print(output)
06895a1d
YW
2454 self.assertRegex(output, '192.168.22.1')
2455 self.assertRegex(output, '6082')
2456 self.assertRegex(output, 'udpcsum')
2457 self.assertRegex(output, 'udp6zerocsumrx')
1f0e3109 2458
5b73edfa
YW
2459 touch_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2460 networkctl_reload()
2461 self.wait_online('geneve99:degraded')
2462
312e3835 2463 def _test_ipip_tunnel(self, mode):
a962d857
YW
2464 copy_network_unit('12-dummy.netdev', '25-ipip.network',
2465 '25-ipip-tunnel.netdev', '25-tunnel.network',
2466 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2467 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2468 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
312e3835
YW
2469
2470 if mode:
2471 for netdev in ['25-ipip-tunnel.netdev',
2472 '25-ipip-tunnel-local-any.netdev',
2473 '25-ipip-tunnel-remote-any.netdev',
2474 '25-ipip-tunnel-any-any.netdev']:
2475 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2476 f.write(f'[Tunnel]\nMode={mode}\n')
2477 else:
2478 mode = 'ipip' # kernel default
2479
2cf6fdff 2480 start_networkd()
95e1fbba 2481 self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
6a97a864 2482
371810d1 2483 output = check_output('ip -d link show ipiptun99')
6a97a864 2484 print(output)
312e3835 2485 self.assertIn(f'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98', output)
371810d1 2486 output = check_output('ip -d link show ipiptun98')
6a97a864 2487 print(output)
312e3835 2488 self.assertIn(f'ipip {mode} remote 192.169.224.239 local any dev dummy98', output)
371810d1 2489 output = check_output('ip -d link show ipiptun97')
6a97a864 2490 print(output)
312e3835 2491 self.assertIn(f'ipip {mode} remote any local 192.168.223.238 dev dummy98', output)
42a29fcb
YW
2492 output = check_output('ip -d link show ipiptun96')
2493 print(output)
312e3835 2494 self.assertIn(f'ipip {mode} remote any local any dev dummy98', output)
1f0e3109 2495
0de55624
YW
2496 touch_network_unit(
2497 '25-ipip-tunnel.netdev',
2498 '25-ipip-tunnel-local-any.netdev',
2499 '25-ipip-tunnel-remote-any.netdev',
2500 '25-ipip-tunnel-any-any.netdev')
2501 networkctl_reload()
2502 self.wait_online(
2503 'ipiptun99:routable',
2504 'ipiptun98:routable',
2505 'ipiptun97:routable',
2506 'ipiptun96:routable',
2507 'dummy98:degraded')
2508
312e3835
YW
2509 def test_ipip_tunnel(self):
2510 first = True
2511 for mode in [None, 'ipip', 'any']:
2512 if first:
2513 first = False
2514 else:
2515 self.tearDown()
2516
2517 print(f'### test_ipip_tunnel(mode={mode})')
2518 with self.subTest(mode=mode):
2519 self._test_ipip_tunnel(mode)
2520
1f0e3109 2521 def test_gre_tunnel(self):
a962d857
YW
2522 copy_network_unit('12-dummy.netdev', '25-gretun.network',
2523 '25-gre-tunnel.netdev', '25-tunnel.network',
2524 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2525 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2526 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2527 start_networkd()
95e1fbba 2528 self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
8f28d342
YW
2529 self.networkctl_check_unit('gretun99', '25-gre-tunnel', '25-tunnel')
2530 self.networkctl_check_unit('gretun98', '25-gre-tunnel-local-any', '25-tunnel-local-any')
2531 self.networkctl_check_unit('gretun97', '25-gre-tunnel-remote-any', '25-tunnel-remote-any')
2532 self.networkctl_check_unit('gretun96', '25-gre-tunnel-any-any', '25-tunnel-any-any')
2533 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretun')
6a97a864 2534
371810d1 2535 output = check_output('ip -d link show gretun99')
6a97a864
YW
2536 print(output)
2537 self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
38f4bb44
YW
2538 self.assertRegex(output, 'ikey 1.2.3.103')
2539 self.assertRegex(output, 'okey 1.2.4.103')
2540 self.assertRegex(output, 'iseq')
2541 self.assertRegex(output, 'oseq')
371810d1 2542 output = check_output('ip -d link show gretun98')
6a97a864
YW
2543 print(output)
2544 self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
38f4bb44
YW
2545 self.assertRegex(output, 'ikey 0.0.0.104')
2546 self.assertRegex(output, 'okey 0.0.0.104')
2547 self.assertNotRegex(output, 'iseq')
2548 self.assertNotRegex(output, 'oseq')
371810d1 2549 output = check_output('ip -d link show gretun97')
6a97a864
YW
2550 print(output)
2551 self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
38f4bb44
YW
2552 self.assertRegex(output, 'ikey 0.0.0.105')
2553 self.assertRegex(output, 'okey 0.0.0.105')
2554 self.assertNotRegex(output, 'iseq')
2555 self.assertNotRegex(output, 'oseq')
42a29fcb
YW
2556 output = check_output('ip -d link show gretun96')
2557 print(output)
2558 self.assertRegex(output, 'gre remote any local any dev dummy98')
2559 self.assertRegex(output, 'ikey 0.0.0.106')
2560 self.assertRegex(output, 'okey 0.0.0.106')
2561 self.assertNotRegex(output, 'iseq')
2562 self.assertNotRegex(output, 'oseq')
6a97a864 2563
0de55624
YW
2564 touch_network_unit(
2565 '25-gre-tunnel.netdev',
2566 '25-gre-tunnel-local-any.netdev',
2567 '25-gre-tunnel-remote-any.netdev',
2568 '25-gre-tunnel-any-any.netdev')
2569 networkctl_reload()
2570 self.wait_online(
2571 'gretun99:routable',
2572 'gretun98:routable',
2573 'gretun97:routable',
2574 'gretun96:routable',
2575 'dummy98:degraded')
2576
6a97a864 2577 def test_ip6gre_tunnel(self):
a962d857
YW
2578 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2579 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2580 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2581 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2582 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2583 start_networkd()
6a97a864 2584
17bcf0a0
YW
2585 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2586
a962d857 2587 self.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
6a97a864 2588
371810d1 2589 output = check_output('ip -d link show ip6gretun99')
6a97a864
YW
2590 print(output)
2591 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2592 output = check_output('ip -d link show ip6gretun98')
6a97a864
YW
2593 print(output)
2594 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
371810d1 2595 output = check_output('ip -d link show ip6gretun97')
6a97a864
YW
2596 print(output)
2597 self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
42a29fcb
YW
2598 output = check_output('ip -d link show ip6gretun96')
2599 print(output)
2600 self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
1f0e3109 2601
0de55624
YW
2602 touch_network_unit(
2603 '25-ip6gre-tunnel.netdev',
2604 '25-ip6gre-tunnel-local-any.netdev',
2605 '25-ip6gre-tunnel-remote-any.netdev',
2606 '25-ip6gre-tunnel-any-any.netdev')
2607 networkctl_reload()
2608 self.wait_links(
2609 'dummy98',
2610 'ip6gretun99',
2611 'ip6gretun98',
2612 'ip6gretun97',
2613 'ip6gretun96')
2614
11309591 2615 def test_gretap_tunnel(self):
a962d857
YW
2616 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2617 '25-gretap-tunnel.netdev', '25-tunnel.network',
2618 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2619 start_networkd()
95e1fbba 2620 self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
8f28d342
YW
2621 self.networkctl_check_unit('gretap99', '25-gretap-tunnel', '25-tunnel')
2622 self.networkctl_check_unit('gretap98', '25-gretap-tunnel-local-any', '25-tunnel-local-any')
2623 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretap')
6a97a864 2624
371810d1 2625 output = check_output('ip -d link show gretap99')
6a97a864
YW
2626 print(output)
2627 self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
38f4bb44
YW
2628 self.assertRegex(output, 'ikey 0.0.0.106')
2629 self.assertRegex(output, 'okey 0.0.0.106')
2630 self.assertRegex(output, 'iseq')
2631 self.assertRegex(output, 'oseq')
b67e8a4e
YZ
2632 self.assertIn('nopmtudisc', output)
2633 self.assertIn('ignore-df', output)
371810d1 2634 output = check_output('ip -d link show gretap98')
6a97a864
YW
2635 print(output)
2636 self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
38f4bb44
YW
2637 self.assertRegex(output, 'ikey 0.0.0.107')
2638 self.assertRegex(output, 'okey 0.0.0.107')
2639 self.assertRegex(output, 'iseq')
2640 self.assertRegex(output, 'oseq')
1f0e3109 2641
0de55624
YW
2642 touch_network_unit(
2643 '25-gretap-tunnel.netdev',
2644 '25-gretap-tunnel-local-any.netdev')
2645 networkctl_reload()
2646 self.wait_online(
2647 'gretap99:routable',
2648 'gretap98:routable',
2649 'dummy98:degraded')
2650
1f0e3109 2651 def test_ip6gretap_tunnel(self):
a962d857
YW
2652 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2653 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2654 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2655 start_networkd()
95e1fbba 2656 self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
8f28d342
YW
2657 self.networkctl_check_unit('ip6gretap99', '25-ip6gretap-tunnel', '25-tunnel')
2658 self.networkctl_check_unit('ip6gretap98', '25-ip6gretap-tunnel-local-any', '25-tunnel-local-any')
2659 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6gretap')
6a97a864 2660
371810d1 2661 output = check_output('ip -d link show ip6gretap99')
6a97a864
YW
2662 print(output)
2663 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2664 output = check_output('ip -d link show ip6gretap98')
6a97a864
YW
2665 print(output)
2666 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
1f0e3109 2667
0de55624
YW
2668 touch_network_unit(
2669 '25-ip6gretap-tunnel.netdev',
2670 '25-ip6gretap-tunnel-local-any.netdev')
2671 networkctl_reload()
2672 self.wait_online(
2673 'ip6gretap99:routable',
2674 'ip6gretap98:routable',
2675 'dummy98:degraded')
2676
1f0e3109 2677 def test_vti_tunnel(self):
a962d857
YW
2678 copy_network_unit('12-dummy.netdev', '25-vti.network',
2679 '25-vti-tunnel.netdev', '25-tunnel.network',
2680 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2681 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2682 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2cf6fdff 2683 start_networkd()
95e1fbba 2684 self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
8f28d342
YW
2685 self.networkctl_check_unit('vtitun99', '25-vti-tunnel', '25-tunnel')
2686 self.networkctl_check_unit('vtitun98', '25-vti-tunnel-local-any', '25-tunnel-local-any')
2687 self.networkctl_check_unit('vtitun97', '25-vti-tunnel-remote-any', '25-tunnel-remote-any')
2688 self.networkctl_check_unit('vtitun96', '25-vti-tunnel-any-any', '25-tunnel-any-any')
2689 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti')
6a97a864 2690
371810d1 2691 output = check_output('ip -d link show vtitun99')
6a97a864
YW
2692 print(output)
2693 self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
371810d1 2694 output = check_output('ip -d link show vtitun98')
6a97a864
YW
2695 print(output)
2696 self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
371810d1 2697 output = check_output('ip -d link show vtitun97')
6a97a864
YW
2698 print(output)
2699 self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
42a29fcb
YW
2700 output = check_output('ip -d link show vtitun96')
2701 print(output)
2702 self.assertRegex(output, 'vti remote any local any dev dummy98')
1f0e3109 2703
0de55624
YW
2704 touch_network_unit(
2705 '25-vti-tunnel.netdev',
2706 '25-vti-tunnel-local-any.netdev',
2707 '25-vti-tunnel-remote-any.netdev',
2708 '25-vti-tunnel-any-any.netdev')
2709 networkctl_reload()
2710 self.wait_online(
2711 'vtitun99:routable',
2712 'vtitun98:routable',
2713 'vtitun97:routable',
2714 'vtitun96:routable',
2715 'dummy98:degraded')
2716
1f0e3109 2717 def test_vti6_tunnel(self):
a962d857
YW
2718 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2719 '25-vti6-tunnel.netdev', '25-tunnel.network',
2720 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2721 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2cf6fdff 2722 start_networkd()
95e1fbba 2723 self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
8f28d342
YW
2724 self.networkctl_check_unit('vti6tun99', '25-vti6-tunnel', '25-tunnel')
2725 self.networkctl_check_unit('vti6tun98', '25-vti6-tunnel-local-any', '25-tunnel-local-any')
2726 self.networkctl_check_unit('vti6tun97', '25-vti6-tunnel-remote-any', '25-tunnel-remote-any')
2727 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti6')
6a97a864 2728
371810d1 2729 output = check_output('ip -d link show vti6tun99')
6a97a864
YW
2730 print(output)
2731 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
371810d1 2732 output = check_output('ip -d link show vti6tun98')
6a97a864 2733 print(output)
426654d7 2734 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
371810d1 2735 output = check_output('ip -d link show vti6tun97')
6a97a864 2736 print(output)
426654d7 2737 self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1f0e3109 2738
0de55624
YW
2739 touch_network_unit(
2740 '25-vti6-tunnel.netdev',
2741 '25-vti6-tunnel-local-any.netdev',
2742 '25-vti6-tunnel-remote-any.netdev')
2743 networkctl_reload()
2744 self.wait_online(
2745 'vti6tun99:routable',
2746 'vti6tun98:routable',
2747 'vti6tun97:routable',
2748 'dummy98:degraded')
2749
312e3835 2750 def _test_ip6tnl_tunnel(self, mode):
a962d857
YW
2751 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2752 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2753 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2754 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2755 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2756 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2757 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
312e3835
YW
2758
2759 if mode:
2760 for netdev in ['25-ip6tnl-tunnel.netdev',
2761 '25-ip6tnl-tunnel-local-any.netdev',
2762 '25-ip6tnl-tunnel-remote-any.netdev',
2763 '25-ip6tnl-tunnel-local-slaac.netdev',
2764 '25-ip6tnl-tunnel-external.netdev']:
2765 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2766 f.write(f'[Tunnel]\nMode={mode}\n')
2767 else:
2768 mode = 'any' # kernel default
2769
2cf6fdff 2770 start_networkd()
95e1fbba
YW
2771 self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2772 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2773 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
8f28d342
YW
2774 self.networkctl_check_unit('ip6tnl99', '25-ip6tnl-tunnel', '25-tunnel')
2775 self.networkctl_check_unit('ip6tnl98', '25-ip6tnl-tunnel-local-any', '25-tunnel-local-any')
2776 self.networkctl_check_unit('ip6tnl97', '25-ip6tnl-tunnel-remote-any', '25-tunnel-remote-any')
2777 self.networkctl_check_unit('ip6tnl-slaac', '25-ip6tnl-tunnel-local-slaac', '25-ip6tnl-tunnel-local-slaac')
2778 self.networkctl_check_unit('ip6tnl-external', '25-ip6tnl-tunnel-external', '26-netdev-link-local-addressing-yes')
2779 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6tnl')
2780 self.networkctl_check_unit('veth99', '25-veth', '25-ip6tnl-slaac')
2781 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
6a97a864 2782
371810d1 2783 output = check_output('ip -d link show ip6tnl99')
6a97a864 2784 print(output)
312e3835 2785 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
371810d1 2786 output = check_output('ip -d link show ip6tnl98')
6a97a864 2787 print(output)
312e3835 2788 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98', output)
371810d1 2789 output = check_output('ip -d link show ip6tnl97')
6a97a864 2790 print(output)
312e3835 2791 self.assertIn(f'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98', output)
7809cab7
YW
2792 output = check_output('ip -d link show ip6tnl-external')
2793 print(output)
2794 self.assertIn('ip6tnl-external@NONE:', output)
2795 self.assertIn('ip6tnl external ', output)
da7d6848
YW
2796 output = check_output('ip -d link show ip6tnl-slaac')
2797 print(output)
312e3835 2798 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
da7d6848
YW
2799
2800 output = check_output('ip -6 address show veth99')
2801 print(output)
2802 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
2803
2804 output = check_output('ip -4 route show default')
2805 print(output)
2806 self.assertIn('default dev ip6tnl-slaac proto static', output)
1f0e3109 2807
0de55624
YW
2808 touch_network_unit(
2809 '25-ip6tnl-tunnel.netdev',
2810 '25-ip6tnl-tunnel-local-any.netdev',
2811 '25-ip6tnl-tunnel-remote-any.netdev',
2812 '25-ip6tnl-tunnel-local-slaac.netdev',
2813 '25-ip6tnl-tunnel-external.netdev')
2814 networkctl_reload()
2815 self.wait_online(
2816 'ip6tnl99:routable',
2817 'ip6tnl98:routable',
2818 'ip6tnl97:routable',
2819 'ip6tnl-slaac:degraded',
2820 'ip6tnl-external:degraded',
2821 'dummy98:degraded',
2822 'veth99:routable',
2823 'veth-peer:degraded')
2824
312e3835
YW
2825 def test_ip6tnl_tunnel(self):
2826 first = True
2827 for mode in [None, 'ipip6', 'ip6ip6', 'any']:
2828 if first:
2829 first = False
2830 else:
2831 self.tearDown()
2832
2833 print(f'### test_ip6tnl_tunnel(mode={mode})')
2834 with self.subTest(mode=mode):
2835 self._test_ip6tnl_tunnel(mode)
2836
2837 def _test_sit_tunnel(self, mode):
a962d857
YW
2838 copy_network_unit('12-dummy.netdev', '25-sit.network',
2839 '25-sit-tunnel.netdev', '25-tunnel.network',
2840 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2841 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2842 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
312e3835
YW
2843
2844 if mode:
2845 for netdev in ['25-sit-tunnel.netdev',
2846 '25-sit-tunnel-local-any.netdev',
2847 '25-sit-tunnel-remote-any.netdev',
2848 '25-sit-tunnel-any-any.netdev']:
2849 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2850 f.write(f'[Tunnel]\nMode={mode}\n')
2851 else:
2852 mode = 'ip6ip' # kernel default
2853
2cf6fdff 2854 start_networkd()
95e1fbba 2855 self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
8f28d342
YW
2856 self.networkctl_check_unit('sittun99', '25-sit-tunnel', '25-tunnel')
2857 self.networkctl_check_unit('sittun98', '25-sit-tunnel-local-any', '25-tunnel-local-any')
2858 self.networkctl_check_unit('sittun97', '25-sit-tunnel-remote-any', '25-tunnel-remote-any')
2859 self.networkctl_check_unit('sittun96', '25-sit-tunnel-any-any', '25-tunnel-any-any')
2860 self.networkctl_check_unit('dummy98', '12-dummy', '25-sit')
6a97a864 2861
371810d1 2862 output = check_output('ip -d link show sittun99')
6a97a864 2863 print(output)
312e3835 2864 self.assertIn(f'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98', output)
371810d1 2865 output = check_output('ip -d link show sittun98')
6a97a864 2866 print(output)
312e3835 2867 self.assertIn(f'sit {mode} remote 10.65.223.239 local any dev dummy98', output)
371810d1 2868 output = check_output('ip -d link show sittun97')
6a97a864 2869 print(output)
312e3835 2870 self.assertIn(f'sit {mode} remote any local 10.65.223.238 dev dummy98', output)
42a29fcb
YW
2871 output = check_output('ip -d link show sittun96')
2872 print(output)
312e3835 2873 self.assertIn(f'sit {mode} remote any local any dev dummy98', output)
1f0e3109 2874
0de55624
YW
2875 touch_network_unit(
2876 '25-sit-tunnel.netdev',
2877 '25-sit-tunnel-local-any.netdev',
2878 '25-sit-tunnel-remote-any.netdev',
2879 '25-sit-tunnel-any-any.netdev')
2880 networkctl_reload()
2881 self.wait_online(
2882 'sittun99:routable',
2883 'sittun98:routable',
2884 'sittun97:routable',
2885 'sittun96:routable',
2886 'dummy98:degraded')
2887
312e3835
YW
2888 def test_sit_tunnel(self):
2889 first = True
2890 for mode in [None, 'ipip', 'ip6ip', 'any']:
2891 if first:
2892 first = False
2893 else:
2894 self.tearDown()
2895
2896 print(f'### test_sit_tunnel(mode={mode})')
2897 with self.subTest(mode=mode):
2898 self._test_sit_tunnel(mode)
2899
d0e728b6 2900 def test_isatap_tunnel(self):
a962d857
YW
2901 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2902 '25-isatap-tunnel.netdev', '25-tunnel.network')
2cf6fdff 2903 start_networkd()
95e1fbba 2904 self.wait_online('isataptun99:routable', 'dummy98:degraded')
8f28d342
YW
2905 self.networkctl_check_unit('isataptun99', '25-isatap-tunnel', '25-tunnel')
2906 self.networkctl_check_unit('dummy98', '12-dummy', '25-isatap')
d0e728b6 2907
371810d1 2908 output = check_output('ip -d link show isataptun99')
14ecd604 2909 print(output)
d0e728b6
SS
2910 self.assertRegex(output, "isatap ")
2911
0de55624
YW
2912 touch_network_unit('25-isatap-tunnel.netdev')
2913 networkctl_reload()
2914 self.wait_online('isataptun99:routable', 'dummy98:degraded')
2915
d29dc4f1 2916 def test_6rd_tunnel(self):
a962d857
YW
2917 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2918 '25-6rd-tunnel.netdev', '25-tunnel.network')
2cf6fdff 2919 start_networkd()
95e1fbba 2920 self.wait_online('sittun99:routable', 'dummy98:degraded')
8f28d342
YW
2921 self.networkctl_check_unit('sittun99', '25-6rd-tunnel', '25-tunnel')
2922 self.networkctl_check_unit('dummy98', '12-dummy', '25-6rd')
d29dc4f1 2923
371810d1 2924 output = check_output('ip -d link show sittun99')
6a97a864
YW
2925 print(output)
2926 self.assertRegex(output, '6rd-prefix 2602::/24')
2927
0de55624
YW
2928 touch_network_unit('25-6rd-tunnel.netdev')
2929 networkctl_reload()
2930 self.wait_online('sittun99:routable', 'dummy98:degraded')
2931
2f0260c1
YW
2932 @expectedFailureIfERSPANv0IsNotSupported()
2933 def test_erspan_tunnel_v0(self):
a962d857
YW
2934 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2935 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2936 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2cf6fdff 2937 start_networkd()
95e1fbba 2938 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
8f28d342
YW
2939 self.networkctl_check_unit('erspan99', '25-erspan0-tunnel', '25-tunnel')
2940 self.networkctl_check_unit('erspan98', '25-erspan0-tunnel-local-any', '25-tunnel-local-any')
2941 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2266864b 2942
371810d1 2943 output = check_output('ip -d link show erspan99')
6a97a864 2944 print(output)
2f0260c1
YW
2945 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2946 self.assertIn('erspan_ver 0', output)
2947 self.assertNotIn('erspan_index 123', output)
2948 self.assertNotIn('erspan_dir ingress', output)
2949 self.assertNotIn('erspan_hwid 1f', output)
2950 self.assertIn('ikey 0.0.0.101', output)
2951 self.assertIn('iseq', output)
b67e8a4e
YZ
2952 self.assertIn('nopmtudisc', output)
2953 self.assertIn('ignore-df', output)
371810d1 2954 output = check_output('ip -d link show erspan98')
2266864b 2955 print(output)
2f0260c1
YW
2956 self.assertIn('erspan remote 172.16.1.100 local any', output)
2957 self.assertIn('erspan_ver 0', output)
2958 self.assertNotIn('erspan_index 124', output)
2959 self.assertNotIn('erspan_dir egress', output)
2960 self.assertNotIn('erspan_hwid 2f', output)
2961 self.assertIn('ikey 0.0.0.102', output)
2962 self.assertIn('iseq', output)
2963
0de55624
YW
2964 touch_network_unit(
2965 '25-erspan0-tunnel.netdev',
2966 '25-erspan0-tunnel-local-any.netdev')
2967 networkctl_reload()
2968 self.wait_online(
2969 'erspan99:routable',
2970 'erspan98:routable',
2971 'dummy98:degraded')
2972
2f0260c1 2973 def test_erspan_tunnel_v1(self):
a962d857
YW
2974 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2975 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2976 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2f0260c1 2977 start_networkd()
95e1fbba 2978 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
8f28d342
YW
2979 self.networkctl_check_unit('erspan99', '25-erspan1-tunnel', '25-tunnel')
2980 self.networkctl_check_unit('erspan98', '25-erspan1-tunnel-local-any', '25-tunnel-local-any')
2981 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2f0260c1
YW
2982
2983 output = check_output('ip -d link show erspan99')
2984 print(output)
2985 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2986 self.assertIn('erspan_ver 1', output)
2987 self.assertIn('erspan_index 123', output)
2988 self.assertNotIn('erspan_dir ingress', output)
2989 self.assertNotIn('erspan_hwid 1f', output)
2990 self.assertIn('ikey 0.0.0.101', output)
2991 self.assertIn('okey 0.0.0.101', output)
2992 self.assertIn('iseq', output)
2993 self.assertIn('oseq', output)
2994 output = check_output('ip -d link show erspan98')
2995 print(output)
2996 self.assertIn('erspan remote 172.16.1.100 local any', output)
2997 self.assertIn('erspan_ver 1', output)
2998 self.assertIn('erspan_index 124', output)
2999 self.assertNotIn('erspan_dir egress', output)
3000 self.assertNotIn('erspan_hwid 2f', output)
3001 self.assertIn('ikey 0.0.0.102', output)
3002 self.assertIn('okey 0.0.0.102', output)
3003 self.assertIn('iseq', output)
3004 self.assertIn('oseq', output)
3005
0de55624
YW
3006 touch_network_unit(
3007 '25-erspan1-tunnel.netdev',
3008 '25-erspan1-tunnel-local-any.netdev')
3009 networkctl_reload()
3010 self.wait_online(
3011 'erspan99:routable',
3012 'erspan98:routable',
3013 'dummy98:degraded')
3014
2f0260c1
YW
3015 @expectedFailureIfERSPANv2IsNotSupported()
3016 def test_erspan_tunnel_v2(self):
a962d857
YW
3017 copy_network_unit('12-dummy.netdev', '25-erspan.network',
3018 '25-erspan2-tunnel.netdev', '25-tunnel.network',
3019 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2f0260c1 3020 start_networkd()
95e1fbba 3021 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
8f28d342
YW
3022 self.networkctl_check_unit('erspan99', '25-erspan2-tunnel', '25-tunnel')
3023 self.networkctl_check_unit('erspan98', '25-erspan2-tunnel-local-any', '25-tunnel-local-any')
3024 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2f0260c1
YW
3025
3026 output = check_output('ip -d link show erspan99')
3027 print(output)
3028 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
3029 self.assertIn('erspan_ver 2', output)
3030 self.assertNotIn('erspan_index 123', output)
3031 self.assertIn('erspan_dir ingress', output)
3032 self.assertIn('erspan_hwid 0x1f', output)
3033 self.assertIn('ikey 0.0.0.101', output)
3034 self.assertIn('okey 0.0.0.101', output)
3035 self.assertIn('iseq', output)
3036 self.assertIn('oseq', output)
3037 output = check_output('ip -d link show erspan98')
3038 print(output)
3039 self.assertIn('erspan remote 172.16.1.100 local any', output)
3040 self.assertIn('erspan_ver 2', output)
3041 self.assertNotIn('erspan_index 124', output)
3042 self.assertIn('erspan_dir egress', output)
3043 self.assertIn('erspan_hwid 0x2f', output)
3044 self.assertIn('ikey 0.0.0.102', output)
3045 self.assertIn('okey 0.0.0.102', output)
3046 self.assertIn('iseq', output)
3047 self.assertIn('oseq', output)
2266864b 3048
0de55624
YW
3049 touch_network_unit(
3050 '25-erspan2-tunnel.netdev',
3051 '25-erspan2-tunnel-local-any.netdev')
3052 networkctl_reload()
3053 self.wait_online(
3054 'erspan99:routable',
3055 'erspan98:routable',
3056 'dummy98:degraded')
3057
1f0e3109 3058 def test_tunnel_independent(self):
a962d857 3059 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 3060 start_networkd()
e40a58b5 3061
95e1fbba 3062 self.wait_online('ipiptun99:carrier')
8f28d342 3063 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent', '26-netdev-link-local-addressing-yes')
1f0e3109 3064
95082dbe 3065 def test_tunnel_independent_loopback(self):
a962d857 3066 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
95082dbe
YW
3067 start_networkd()
3068
95e1fbba 3069 self.wait_online('ipiptun99:carrier')
8f28d342 3070 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent-loopback', '26-netdev-link-local-addressing-yes')
95082dbe 3071
e64dc406
YW
3072 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
3073 def test_xfrm(self):
a962d857
YW
3074 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
3075 '25-xfrm.netdev', '25-xfrm-independent.netdev',
3076 '26-netdev-link-local-addressing-yes.network')
e64dc406
YW
3077 start_networkd()
3078
95e1fbba 3079 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
8f28d342
YW
3080 self.networkctl_check_unit('dummy98', '12-dummy', '25-xfrm')
3081 self.networkctl_check_unit('xfrm98', '25-xfrm', '26-netdev-link-local-addressing-yes')
3082 self.networkctl_check_unit('xfrm99', '25-xfrm-independent', '26-netdev-link-local-addressing-yes')
e64dc406 3083
020483b2 3084 output = check_output('ip -d link show dev xfrm98')
e64dc406 3085 print(output)
020483b2
YW
3086 self.assertIn('xfrm98@dummy98:', output)
3087 self.assertIn('xfrm if_id 0x98 ', output)
e64dc406 3088
020483b2
YW
3089 output = check_output('ip -d link show dev xfrm99')
3090 print(output)
3091 self.assertIn('xfrm99@lo:', output)
3092 self.assertIn('xfrm if_id 0x99 ', output)
e64dc406 3093
0de55624
YW
3094 touch_network_unit('25-xfrm.netdev')
3095 networkctl_reload()
3096 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
3097
4b6a6d1e
YW
3098 @expectedFailureIfModuleIsNotAvailable('fou')
3099 def test_fou(self):
a962d857
YW
3100 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3101 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3102 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2cf6fdff 3103 start_networkd()
4b6a6d1e 3104
95e1fbba 3105 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
8f28d342
YW
3106 self.networkctl_check_unit('ipiptun96', '25-fou-ipip')
3107 self.networkctl_check_unit('sittun96', '25-fou-sit')
3108 self.networkctl_check_unit('gretun96', '25-fou-gre')
3109 self.networkctl_check_unit('gretap96', '25-fou-gretap')
4b6a6d1e 3110
371810d1 3111 output = check_output('ip fou show')
4b6a6d1e
YW
3112 print(output)
3113 self.assertRegex(output, 'port 55555 ipproto 4')
3114 self.assertRegex(output, 'port 55556 ipproto 47')
3115
371810d1 3116 output = check_output('ip -d link show ipiptun96')
4b6a6d1e
YW
3117 print(output)
3118 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
371810d1 3119 output = check_output('ip -d link show sittun96')
4b6a6d1e
YW
3120 print(output)
3121 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
371810d1 3122 output = check_output('ip -d link show gretun96')
4b6a6d1e
YW
3123 print(output)
3124 self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
371810d1 3125 output = check_output('ip -d link show gretap96')
4b6a6d1e
YW
3126 print(output)
3127 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
3128
5b73edfa
YW
3129 touch_network_unit(
3130 '25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3131 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3132 '25-fou-gre.netdev', '25-fou-gretap.netdev')
3133 networkctl_reload()
3134 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
3135
1f0e3109 3136 def test_vxlan(self):
a962d857
YW
3137 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
3138 '25-vxlan.netdev', '25-vxlan.network',
3139 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
3140 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
3141 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
6e529860
EL
3142 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network',
3143 '25-vxlan-external.netdev', '25-vxlan-external.network')
2cf6fdff 3144 start_networkd()
1f0e3109 3145
95e1fbba 3146 self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
6e529860
EL
3147 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded',
3148 'vxlan-external:degraded')
8f28d342
YW
3149 self.networkctl_check_unit('test1', '11-dummy', '25-vxlan-test1')
3150 self.networkctl_check_unit('veth99', '25-veth', '25-vxlan-veth99')
3151 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
3152 self.networkctl_check_unit('vxlan99', '25-vxlan', '25-vxlan')
3153 self.networkctl_check_unit('vxlan98', '25-vxlan-independent', '26-netdev-link-local-addressing-yes')
3154 self.networkctl_check_unit('vxlan97', '25-vxlan-ipv6', '25-vxlan-ipv6')
3155 self.networkctl_check_unit('vxlan-slaac', '25-vxlan-local-slaac', '25-vxlan-local-slaac')
6e529860 3156 self.networkctl_check_unit('vxlan-external', '25-vxlan-external', '25-vxlan-external')
1f0e3109 3157
a5e478b2 3158 output = check_output('ip -d -d link show vxlan99')
14ecd604 3159 print(output)
cca07d91
YW
3160 self.assertIn('999', output)
3161 self.assertIn('5555', output)
3162 self.assertIn('l2miss', output)
3163 self.assertIn('l3miss', output)
cca07d91 3164 self.assertIn('gbp', output)
a5e478b2
FS
3165 # Since [0] some of the options use slightly different names and some
3166 # options with default values are shown only if the -d(etails) setting
3167 # is repeated
3168 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
3169 self.assertRegex(output, '(udpcsum|udp_csum)')
3170 self.assertRegex(output, '(udp6zerocsumtx|udp_zero_csum6_tx)')
3171 self.assertRegex(output, '(udp6zerocsumrx|udp_zero_csum6_rx)')
3172 self.assertRegex(output, '(remcsumtx|remcsum_tx)')
3173 self.assertRegex(output, '(remcsumrx|remcsum_rx)')
1f0e3109 3174
371810d1 3175 output = check_output('bridge fdb show dev vxlan99')
1c862fe0 3176 print(output)
cca07d91
YW
3177 self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
3178 self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
3179 self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
1c862fe0 3180
10d670a3 3181 output = networkctl_status('vxlan99')
36bc2ffb 3182 print(output)
cca07d91
YW
3183 self.assertIn('VNI: 999', output)
3184 self.assertIn('Destination Port: 5555', output)
3185 self.assertIn('Underlying Device: test1', output)
3186
3187 output = check_output('bridge fdb show dev vxlan97')
3188 print(output)
3189 self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
3190 self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
3191 self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
36bc2ffb 3192
49ad8da7
YW
3193 output = check_output('ip -d link show vxlan-slaac')
3194 print(output)
3195 self.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
3196
3197 output = check_output('ip -6 address show veth99')
3198 print(output)
3199 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
3200
6e529860
EL
3201 output = check_output('ip -d link show vxlan-external')
3202 print(output)
3203 self.assertIn('id 0 ', output)
3204 self.assertIn('external', output)
3205 self.assertIn('vnifilter', output)
3206
b3de9d7b 3207 @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
02849d8b 3208 def test_macsec(self):
a962d857
YW
3209 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
3210 '26-macsec.network', '12-dummy.netdev')
2cf6fdff 3211 start_networkd()
02849d8b 3212
95e1fbba 3213 self.wait_online('dummy98:degraded', 'macsec99:routable')
8f28d342
YW
3214 self.networkctl_check_unit('dummy98', '12-dummy', '26-macsec')
3215 self.networkctl_check_unit('macsec99', '25-macsec', '25-macsec')
02849d8b 3216
371810d1 3217 output = check_output('ip -d link show macsec99')
02849d8b
YW
3218 print(output)
3219 self.assertRegex(output, 'macsec99@dummy98')
3220 self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
3221 self.assertRegex(output, 'encrypt on')
3222
371810d1 3223 output = check_output('ip macsec show macsec99')
02849d8b
YW
3224 print(output)
3225 self.assertRegex(output, 'encrypt on')
3226 self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
3227 self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
3228 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
3229 self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
3230 self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
3231 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
3232 self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
3233 self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
3234 self.assertNotRegex(output, 'key 02030405067080900000000000000000')
3235 self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
3236 self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
3237
0de55624
YW
3238 touch_network_unit('25-macsec.netdev')
3239 networkctl_reload()
3240 self.wait_online('dummy98:degraded', 'macsec99:routable')
3241
811f33d0 3242 def test_nlmon(self):
a962d857 3243 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2cf6fdff 3244 start_networkd()
811f33d0 3245
95e1fbba 3246 self.wait_online('nlmon99:carrier')
8f28d342 3247 self.networkctl_check_unit('nlmon99', '25-nlmon', '26-netdev-link-local-addressing-yes')
02849d8b 3248
5b73edfa
YW
3249 touch_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
3250 networkctl_reload()
3251 self.wait_online('nlmon99:carrier')
3252
b076d5d7
YW
3253 @expectedFailureIfModuleIsNotAvailable('ifb')
3254 def test_ifb(self):
a962d857 3255 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
b076d5d7
YW
3256 start_networkd()
3257
95e1fbba 3258 self.wait_online('ifb99:degraded')
8f28d342 3259 self.networkctl_check_unit('ifb99', '25-ifb', '26-netdev-link-local-addressing-yes')
b076d5d7 3260
5b73edfa
YW
3261 touch_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
3262 networkctl_reload()
3263 self.wait_online('ifb99:degraded')
3264
a6f5673c
RRZ
3265 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3266 def test_rps_cpu_1(self):
4b35dab8 3267 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
a6f5673c
RRZ
3268 start_networkd()
3269
4b35dab8 3270 self.wait_online('dummy98:carrier')
8f28d342 3271 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-1')
a6f5673c
RRZ
3272
3273 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3274 print(output)
3275 self.assertEqual(int(output.replace(',', ''), base=16), 2)
3276
3277 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3278 def test_rps_cpu_0_1(self):
4b35dab8 3279 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
a6f5673c
RRZ
3280 start_networkd()
3281
4b35dab8 3282 self.wait_online('dummy98:carrier')
8f28d342 3283 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-1')
a6f5673c
RRZ
3284
3285 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3286 print(output)
3287 self.assertEqual(int(output.replace(',', ''), base=16), 3)
3288
3289 @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test")
3290 def test_rps_cpu_multi(self):
4b35dab8 3291 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
a6f5673c
RRZ
3292 start_networkd()
3293
4b35dab8 3294 self.wait_online('dummy98:carrier')
8f28d342 3295 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-multi')
a6f5673c
RRZ
3296
3297 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3298 print(output)
3299 self.assertEqual(int(output.replace(',', ''), base=16), 15)
3300
4b35dab8 3301 def test_rps_cpu(self):
a6f5673c
RRZ
3302 cpu_count = os.cpu_count()
3303
4b35dab8 3304 copy_network_unit('12-dummy.netdev', '12-dummy.network')
a6f5673c
RRZ
3305 start_networkd()
3306
4b35dab8 3307 self.wait_online('dummy98:carrier')
8f28d342 3308 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy')
a6f5673c 3309
4b35dab8
YW
3310 # 0
3311 copy_network_unit('25-rps-cpu-0.link')
3312 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3313 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0')
a6f5673c
RRZ
3314 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3315 print(output)
4b35dab8
YW
3316 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3317 remove_network_unit('25-rps-cpu-0.link')
a6f5673c 3318
4b35dab8
YW
3319 # all
3320 copy_network_unit('25-rps-cpu-all.link')
3321 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3322 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
a6f5673c
RRZ
3323 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3324 print(output)
4b35dab8
YW
3325 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3326 remove_network_unit('25-rps-cpu-all.link')
a6f5673c 3327
4b35dab8
YW
3328 # disable
3329 copy_network_unit('24-rps-cpu-disable.link')
3330 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3331 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-disable')
a6f5673c
RRZ
3332 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3333 print(output)
3334 self.assertEqual(int(output.replace(',', ''), base=16), 0)
4b35dab8 3335 remove_network_unit('24-rps-cpu-disable.link')
a6f5673c 3336
4b35dab8
YW
3337 # set all again
3338 copy_network_unit('25-rps-cpu-all.link')
3339 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3340 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
a6f5673c
RRZ
3341 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3342 print(output)
4b35dab8
YW
3343 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3344 remove_network_unit('25-rps-cpu-all.link')
a6f5673c 3345
4b35dab8
YW
3346 # empty -> unchanged
3347 copy_network_unit('24-rps-cpu-empty.link')
3348 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3349 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-empty')
a6f5673c
RRZ
3350 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3351 print(output)
4b35dab8
YW
3352 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3353 remove_network_unit('24-rps-cpu-empty.link')
a6f5673c 3354
4b35dab8
YW
3355 # 0, then empty -> unchanged
3356 copy_network_unit('25-rps-cpu-0-empty.link')
3357 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3358 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-empty')
a6f5673c
RRZ
3359 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3360 print(output)
4b35dab8
YW
3361 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3362 remove_network_unit('25-rps-cpu-0-empty.link')
a6f5673c 3363
4b35dab8
YW
3364 # 0, then invalid -> 0
3365 copy_network_unit('25-rps-cpu-0-invalid.link')
3366 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3367 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-invalid')
a6f5673c
RRZ
3368 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3369 print(output)
3370 self.assertEqual(int(output.replace(',', ''), base=16), 1)
4b35dab8 3371 remove_network_unit('25-rps-cpu-0-invalid.link')
a6f5673c 3372
4b35dab8
YW
3373 # invalid -> unchanged
3374 copy_network_unit('24-rps-cpu-invalid.link')
3375 udevadm_trigger('/sys/class/net/dummy98')
8f28d342 3376 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-invalid')
a6f5673c
RRZ
3377 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3378 print(output)
4b35dab8
YW
3379 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3380 remove_network_unit('24-rps-cpu-invalid.link')
a6f5673c 3381
cff83db9
YW
3382class NetworkdL2TPTests(unittest.TestCase, Utilities):
3383
cff83db9 3384 def setUp(self):
a962d857 3385 setup_common()
cff83db9
YW
3386
3387 def tearDown(self):
a962d857 3388 tear_down_common()
cff83db9 3389
59edcf2b 3390 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
cff83db9 3391 def test_l2tp_udp(self):
a962d857
YW
3392 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3393 '25-l2tp-udp.netdev', '25-l2tp.network')
2cf6fdff 3394 start_networkd()
cff83db9 3395
95e1fbba 3396 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
8f28d342
YW
3397 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3398 self.networkctl_check_unit('l2tp-ses1', '25-l2tp-udp', '25-l2tp')
3399 self.networkctl_check_unit('l2tp-ses2', '25-l2tp-udp', '25-l2tp')
cff83db9 3400
371810d1 3401 output = check_output('ip l2tp show tunnel tunnel_id 10')
cff83db9
YW
3402 print(output)
3403 self.assertRegex(output, "Tunnel 10, encap UDP")
3404 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3405 self.assertRegex(output, "Peer tunnel 11")
3406 self.assertRegex(output, "UDP source / dest ports: 3000/4000")
3407 self.assertRegex(output, "UDP checksum: enabled")
3408
371810d1 3409 output = check_output('ip l2tp show session tid 10 session_id 15')
cff83db9
YW
3410 print(output)
3411 self.assertRegex(output, "Session 15 in tunnel 10")
3412 self.assertRegex(output, "Peer session 16, tunnel 11")
3413 self.assertRegex(output, "interface name: l2tp-ses1")
3414
371810d1 3415 output = check_output('ip l2tp show session tid 10 session_id 17')
cff83db9
YW
3416 print(output)
3417 self.assertRegex(output, "Session 17 in tunnel 10")
3418 self.assertRegex(output, "Peer session 18, tunnel 11")
3419 self.assertRegex(output, "interface name: l2tp-ses2")
3420
5b73edfa
YW
3421 touch_network_unit(
3422 '11-dummy.netdev', '25-l2tp-dummy.network',
3423 '25-l2tp-udp.netdev', '25-l2tp.network')
3424 networkctl_reload()
3425 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
3426
59edcf2b 3427 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
cff83db9 3428 def test_l2tp_ip(self):
a962d857
YW
3429 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3430 '25-l2tp-ip.netdev', '25-l2tp.network')
2cf6fdff 3431 start_networkd()
cff83db9 3432
95e1fbba 3433 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
8f28d342
YW
3434 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3435 self.networkctl_check_unit('l2tp-ses3', '25-l2tp-ip', '25-l2tp')
3436 self.networkctl_check_unit('l2tp-ses4', '25-l2tp-ip', '25-l2tp')
cff83db9 3437
371810d1 3438 output = check_output('ip l2tp show tunnel tunnel_id 10')
cff83db9
YW
3439 print(output)
3440 self.assertRegex(output, "Tunnel 10, encap IP")
3441 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3442 self.assertRegex(output, "Peer tunnel 12")
3443
371810d1 3444 output = check_output('ip l2tp show session tid 10 session_id 25')
cff83db9
YW
3445 print(output)
3446 self.assertRegex(output, "Session 25 in tunnel 10")
3447 self.assertRegex(output, "Peer session 26, tunnel 12")
3448 self.assertRegex(output, "interface name: l2tp-ses3")
3449
371810d1 3450 output = check_output('ip l2tp show session tid 10 session_id 27')
cff83db9
YW
3451 print(output)
3452 self.assertRegex(output, "Session 27 in tunnel 10")
3453 self.assertRegex(output, "Peer session 28, tunnel 12")
3454 self.assertRegex(output, "interface name: l2tp-ses4")
3455
5b73edfa
YW
3456 touch_network_unit(
3457 '11-dummy.netdev', '25-l2tp-dummy.network',
3458 '25-l2tp-ip.netdev', '25-l2tp.network')
3459 networkctl_reload()
3460 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
3461
be68c2c9 3462class NetworkdNetworkTests(unittest.TestCase, Utilities):
95c74b0a 3463
1f0e3109 3464 def setUp(self):
a962d857 3465 setup_common()
1f0e3109
SS
3466
3467 def tearDown(self):
a962d857 3468 tear_down_common()
1f0e3109 3469
78f8d5ed
YW
3470 def test_ID_NET_MANAGED_BY(self):
3471 copy_network_unit('11-dummy.netdev', '11-dummy-unmanaged.link', '11-dummy.network')
3472 start_networkd()
3473 self.wait_online('test1:off', setup_state='unmanaged')
3474
3475 check_output('ip link set dev test1 up')
3476 self.wait_online('test1:degraded', setup_state='unmanaged')
3477
3478 check_output('ip link set dev test1 down')
3479 self.wait_online('test1:off', setup_state='unmanaged')
3480
f2bcd324
YW
3481 def verify_address_static(
3482 self,
3483 label1: str,
3484 label2: str,
3485 label3: str,
3486 broadcast1: str,
3487 broadcast2: str,
3488 broadcast3: str,
3489 peer1: str,
3490 peer2: str,
3491 peer3: str,
3492 peer4: str,
3493 peer5: str,
3494 peer6: str,
3495 scope1: str,
3496 scope2: str,
3497 deprecated1: str,
3498 deprecated2: str,
3499 deprecated3: str,
3500 deprecated4: str,
3501 route_metric: int,
3502 flag1: str,
3503 flag2: str,
3504 flag3: str,
3505 flag4: str,
d5adff70
YW
3506 ip4_null_16: str,
3507 ip4_null_24: str,
3508 ip6_null_73: str,
3509 ip6_null_74: str,
f2bcd324
YW
3510 ):
3511 output = check_output('ip address show dev dummy98')
3512 print(output)
3513
3514 # simple settings
4a704501
YW
3515 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
3516 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
3517 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
4a704501
YW
3518 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
3519 self.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output)
3520 self.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output)
f2bcd324
YW
3521
3522 # label
3523 self.assertIn(f'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output)
3524 self.assertIn(f'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output)
3525 self.assertIn(f'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output)
3526
3527 # broadcast
3528 self.assertIn(f'inet 10.4.1.1/24{broadcast1} scope global dummy98', output)
3529 self.assertIn(f'inet 10.4.2.1/24{broadcast2} scope global dummy98', output)
3530 self.assertIn(f'inet 10.4.3.1/24{broadcast3} scope global dummy98', output)
3531
3532 # peer
3533 self.assertIn(f'inet 10.5.1.1{peer1} scope global dummy98', output)
3534 self.assertIn(f'inet 10.5.2.1{peer2} scope global dummy98', output)
3535 self.assertIn(f'inet 10.5.3.1{peer3} scope global dummy98', output)
3536 self.assertIn(f'inet6 2001:db8:0:f103::1{peer4} scope global', output)
3537 self.assertIn(f'inet6 2001:db8:0:f103::2{peer5} scope global', output)
3538 self.assertIn(f'inet6 2001:db8:0:f103::3{peer6} scope global', output)
3539
3540 # scope
3541 self.assertIn(f'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output)
3542 self.assertIn(f'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output)
3543
3544 # lifetime
3545 self.assertIn(f'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output)
3546 self.assertIn(f'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output)
3547 self.assertIn(f'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output)
3548 self.assertIn(f'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output)
3549
3550 # route metric
3551 self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
3552 self.assertRegex(output, rf'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
3553
3554 output_route = check_output('ip -4 route show dev dummy98 10.8.1.0/24')
3555 print(output_route)
3556 self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route)
3557
3558 output_route = check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
3559 print(output_route)
3560 self.assertIn(f'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route)
3561
3562 # flags
3563 self.assertIn(f'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output)
3564 self.assertIn(f'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output)
3565 self.assertIn(f'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output)
3566 self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
3567
3568 # null address
d5adff70
YW
3569 self.assertTrue(ip4_null_16.endswith('.0.1'))
3570 prefix16 = ip4_null_16[:-len('.0.1')]
3571 self.assertTrue(ip4_null_24.endswith('.1'))
3572 prefix24 = ip4_null_24[:-len('.1')]
3573 self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
3574 self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
3575 self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
3576 self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
f2bcd324
YW
3577
3578 # invalid sections
3579 self.assertNotIn('10.4.4.1', output)
3580 self.assertNotIn('10.5.4.1', output)
3581 self.assertNotIn('10.5.5.1', output)
3582 self.assertNotIn('10.8.2.1', output)
3583 self.assertNotIn('10.9.3.1', output)
3584 self.assertNotIn('2001:db8:0:f101::2', output)
3585 self.assertNotIn('2001:db8:0:f103::4', output)
b8102725 3586
d19704cd 3587 # netlabel
f2bcd324 3588 self.check_netlabel('dummy98', r'10\.10\.1\.0/24')
a4640bed 3589
10d670a3 3590 check_json(networkctl_json())
d19704cd 3591
d22f2fb9 3592 @expectedFailureIfKernelReturnsInvalidFlags()
d19704cd 3593 def test_address_static(self):
d19704cd 3594 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
c742d7e8
TM
3595 self.setup_nftset('addr4', 'ipv4_addr')
3596 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
3597 self.setup_nftset('ifindex', 'iface_index')
1ce2ffac 3598 start_networkd()
d19704cd 3599
95e1fbba 3600 self.wait_online('dummy98:routable')
d5adff70
YW
3601
3602 ip4_null_16 = None
3603 ip4_null_24 = None
3604 output = check_output('ip -4 --json address show dev dummy98')
3605 for i in json.loads(output)[0]['addr_info']:
3606 if i['label'] == 'subnet16':
3607 ip4_null_16 = i['local']
3608 elif i['label'] == 'subnet24':
3609 ip4_null_24 = i['local']
3610 self.assertTrue(ip4_null_16.endswith('.0.1'))
3611 self.assertTrue(ip4_null_24.endswith('.1'))
3612
3613 ip6_null_73 = None
3614 ip6_null_74 = None
3615 output = check_output('ip -6 --json address show dev dummy98')
3616 for i in json.loads(output)[0]['addr_info']:
3617 if i['prefixlen'] == 73:
3618 ip6_null_73 = i['local']
3619 elif i['prefixlen'] == 74:
3620 ip6_null_74 = i['local']
3621 self.assertTrue(ip6_null_73.endswith(':1'))
3622 self.assertTrue(ip6_null_74.endswith(':1'))
3623
f2bcd324
YW
3624 self.verify_address_static(
3625 label1='label1',
3626 label2='label2',
3627 label3='dummy98',
3628 broadcast1='',
3629 broadcast2=' brd 10.4.2.255',
3630 broadcast3=' brd 10.4.3.63',
3631 peer1=' peer 10.5.1.101/24',
3632 peer2=' peer 10.5.2.101/24',
3633 peer3='/24 brd 10.5.3.255',
3634 peer4=' peer 2001:db8:0:f103::101/128',
3635 peer5=' peer 2001:db8:0:f103::102/128',
3636 peer6='/128',
3637 scope1='global',
3638 scope2='link',
3639 deprecated1='',
3640 deprecated2=' deprecated',
3641 deprecated3='',
3642 deprecated4=' deprecated',
3643 route_metric=128,
3644 flag1=' noprefixroute',
3645 flag2='',
3646 flag3=' noprefixroute',
3647 flag4=' home mngtmpaddr',
d5adff70
YW
3648 ip4_null_16=ip4_null_16,
3649 ip4_null_24=ip4_null_24,
3650 ip6_null_73=ip6_null_73,
3651 ip6_null_74=ip6_null_74,
f2bcd324 3652 )
c742d7e8 3653 # nft set
f432aa90
TM
3654 self.check_nftset('addr4', r'10\.10\.1\.1')
3655 self.check_nftset('network4', r'10\.10\.1\.0/24')
3656 self.check_nftset('ifindex', 'dummy98')
c742d7e8
TM
3657
3658 self.teardown_nftset('addr4', 'network4', 'ifindex')
f2bcd324
YW
3659
3660 copy_network_unit('25-address-static.network.d/10-override.conf')
d19704cd 3661 networkctl_reload()
95e1fbba 3662 self.wait_online('dummy98:routable')
f2bcd324
YW
3663 self.verify_address_static(
3664 label1='new-label1',
3665 label2='dummy98',
3666 label3='new-label3',
3667 broadcast1=' brd 10.4.1.255',
3668 broadcast2='',
3669 broadcast3=' brd 10.4.3.31',
3670 peer1=' peer 10.5.1.102/24',
3671 peer2='/24 brd 10.5.2.255',
3672 peer3=' peer 10.5.3.102/24',
3673 peer4=' peer 2001:db8:0:f103::201/128',
3674 peer5='/128',
3675 peer6=' peer 2001:db8:0:f103::203/128',
3676 scope1='link',
3677 scope2='global',
3678 deprecated1=' deprecated',
3679 deprecated2='',
3680 deprecated3=' deprecated',
3681 deprecated4='',
3682 route_metric=256,
3683 flag1='',
3684 flag2=' noprefixroute',
3685 flag3=' home mngtmpaddr',
3686 flag4=' noprefixroute',
d5adff70
YW
3687 ip4_null_16=ip4_null_16,
3688 ip4_null_24=ip4_null_24,
3689 ip6_null_73=ip6_null_73,
3690 ip6_null_74=ip6_null_74,
f2bcd324 3691 )
d19704cd
YW
3692
3693 networkctl_reconfigure('dummy98')
95e1fbba 3694 self.wait_online('dummy98:routable')
f2bcd324
YW
3695 self.verify_address_static(
3696 label1='new-label1',
3697 label2='dummy98',
3698 label3='new-label3',
3699 broadcast1=' brd 10.4.1.255',
3700 broadcast2='',
3701 broadcast3=' brd 10.4.3.31',
3702 peer1=' peer 10.5.1.102/24',
3703 peer2='/24 brd 10.5.2.255',
3704 peer3=' peer 10.5.3.102/24',
3705 peer4=' peer 2001:db8:0:f103::201/128',
3706 peer5='/128',
3707 peer6=' peer 2001:db8:0:f103::203/128',
3708 scope1='link',
3709 scope2='global',
3710 deprecated1=' deprecated',
3711 deprecated2='',
3712 deprecated3=' deprecated',
3713 deprecated4='',
3714 route_metric=256,
3715 flag1='',
3716 flag2=' noprefixroute',
3717 flag3=' home mngtmpaddr',
3718 flag4=' noprefixroute',
d5adff70
YW
3719 ip4_null_16=ip4_null_16,
3720 ip4_null_24=ip4_null_24,
3721 ip6_null_73=ip6_null_73,
3722 ip6_null_74=ip6_null_74,
f2bcd324 3723 )
d19704cd 3724
40971657
YW
3725 # Tests for #20891.
3726 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
f2bcd324
YW
3727 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
3728 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
3729 output = check_output('ip address show dev dummy98')
40971657 3730 print(output)
f2bcd324
YW
3731 self.assertNotRegex(output, '10.7.1.1/24 .* deprecated')
3732 self.assertNotRegex(output, '2001:db8:0:f104::1/64 .* deprecated')
40971657 3733
f2bcd324 3734 # 2. reconfigure the interface, and check the deprecated flag is set again
a962d857 3735 networkctl_reconfigure('dummy98')
95e1fbba 3736 self.wait_online('dummy98:routable')
f2bcd324
YW
3737 self.verify_address_static(
3738 label1='new-label1',
3739 label2='dummy98',
3740 label3='new-label3',
3741 broadcast1=' brd 10.4.1.255',
3742 broadcast2='',
3743 broadcast3=' brd 10.4.3.31',
3744 peer1=' peer 10.5.1.102/24',
3745 peer2='/24 brd 10.5.2.255',
3746 peer3=' peer 10.5.3.102/24',
3747 peer4=' peer 2001:db8:0:f103::201/128',
3748 peer5='/128',
3749 peer6=' peer 2001:db8:0:f103::203/128',
3750 scope1='link',
3751 scope2='global',
3752 deprecated1=' deprecated',
3753 deprecated2='',
3754 deprecated3=' deprecated',
3755 deprecated4='',
3756 route_metric=256,
3757 flag1='',
3758 flag2=' noprefixroute',
3759 flag3=' home mngtmpaddr',
3760 flag4=' noprefixroute',
d5adff70
YW
3761 ip4_null_16=ip4_null_16,
3762 ip4_null_24=ip4_null_24,
3763 ip6_null_73=ip6_null_73,
3764 ip6_null_74=ip6_null_74,
f2bcd324 3765 )
40971657 3766
d19704cd
YW
3767 # test for ENOBUFS issue #17012 (with reload)
3768 copy_network_unit('25-address-static.network.d/10-many-address.conf')
3769 networkctl_reload()
95e1fbba 3770 self.wait_online('dummy98:routable')
766f8f38 3771 output = check_output('ip -4 address show dev dummy98')
a962d857 3772 for i in range(1, 254):
4a704501 3773 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
e4783b54 3774
d19704cd
YW
3775 # (with reconfigure)
3776 networkctl_reconfigure('dummy98')
95e1fbba 3777 self.wait_online('dummy98:routable')
d19704cd
YW
3778 output = check_output('ip -4 address show dev dummy98')
3779 for i in range(1, 254):
3780 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3a2ef59d
YW
3781
3782 # test for an empty string assignment for Address= in [Network]
3783 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
3784 networkctl_reload()
95e1fbba 3785 self.wait_online('dummy98:routable')
3a2ef59d
YW
3786 output = check_output('ip -4 address show dev dummy98')
3787 for i in range(1, 254):
3788 self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3789 self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
146726b2 3790
44924431
YW
3791 def test_address_ipv4acd(self):
3792 check_output('ip netns add ns99')
3793 check_output('ip link add veth99 type veth peer veth-peer')
3794 check_output('ip link set veth-peer netns ns99')
3795 check_output('ip link set veth99 up')
3796 check_output('ip netns exec ns99 ip link set veth-peer up')
3797 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
3798
a962d857 3799 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False)
dc7d3c5f 3800 start_networkd()
95e1fbba 3801 self.wait_online('veth99:routable')
dc7d3c5f
YW
3802
3803 output = check_output('ip -4 address show dev veth99')
3804 print(output)
44924431
YW
3805 self.assertNotIn('192.168.100.10/24', output)
3806 self.assertIn('192.168.100.11/24', output)
dc7d3c5f 3807
a962d857
YW
3808 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
3809 networkctl_reload()
37611ccb 3810 self.wait_operstate('veth99', operstate='routable', setup_state='configuring', setup_timeout=10)
44924431
YW
3811
3812 output = check_output('ip -4 address show dev veth99')
dc7d3c5f 3813 print(output)
44924431
YW
3814 self.assertNotIn('192.168.100.10/24', output)
3815 self.assertIn('192.168.100.11/24', output)
dc7d3c5f 3816
21266e60
YW
3817 def test_address_peer_ipv4(self):
3818 # test for issue #17304
a962d857 3819 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
21266e60
YW
3820
3821 for trial in range(2):
3822 if trial == 0:
3823 start_networkd()
3824 else:
3825 restart_networkd()
3826
95e1fbba 3827 self.wait_online('dummy98:routable')
21266e60
YW
3828
3829 output = check_output('ip -4 address show dev dummy98')
3830 self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
3831
c9d223e8
YW
3832 @expectedFailureIfModuleIsNotAvailable('vrf')
3833 def test_prefix_route(self):
a962d857
YW
3834 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3835 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3836 '25-vrf.netdev', '25-vrf.network')
c9d223e8
YW
3837 for trial in range(2):
3838 if trial == 0:
3839 start_networkd()
3840 else:
a962d857 3841 restart_networkd()
c9d223e8 3842
95e1fbba 3843 self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
c9d223e8
YW
3844
3845 output = check_output('ip route show table 42 dev dummy98')
3846 print('### ip route show table 42 dev dummy98')
3847 print(output)
3848 self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
c9d223e8
YW
3849 self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3850 self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3851 self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3852 self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
c9d223e8
YW
3853 self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3854 self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3855 output = check_output('ip -6 route show table 42 dev dummy98')
3856 print('### ip -6 route show table 42 dev dummy98')
3857 print(output)
3858 if trial == 0:
3859 # Kernel's bug?
3860 self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3861 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3862 self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3863 self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3864 self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3865 self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3866 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
beb75dd3 3867 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
c9d223e8
YW
3868
3869 print()
3870
3871 output = check_output('ip route show dev test1')
3872 print('### ip route show dev test1')
3873 print(output)
3874 self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3875 output = check_output('ip route show table local dev test1')
3876 print('### ip route show table local dev test1')
3877 print(output)
3878 self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
c9d223e8
YW
3879 self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3880 self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3881 self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
c9d223e8
YW
3882 self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3883 self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3884 output = check_output('ip -6 route show dev test1')
3885 print('### ip -6 route show dev test1')
3886 print(output)
3887 self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3888 self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3889 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3890 output = check_output('ip -6 route show table local dev test1')
3891 print('### ip -6 route show table local dev test1')
3892 print(output)
3893 self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3894 self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3895 self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3896 self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
beb75dd3 3897 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
c9d223e8 3898
1f0e3109 3899 def test_configure_without_carrier(self):
a962d857 3900 copy_network_unit('11-dummy.netdev')
2cf6fdff 3901 start_networkd()
9bacf431
DS
3902 self.wait_operstate('test1', 'off', '')
3903 check_output('ip link set dev test1 up carrier off')
3904
a962d857 3905 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False)
9bacf431 3906 restart_networkd()
95e1fbba 3907 self.wait_online('test1:no-carrier')
9bacf431
DS
3908
3909 carrier_map = {'on': '1', 'off': '0'}
3910 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3911 for carrier in ['off', 'on', 'off']:
3912 with self.subTest(carrier=carrier):
3913 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3914 check_output(f'ip link set dev test1 carrier {carrier}')
95e1fbba 3915 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
9bacf431 3916
10d670a3 3917 output = networkctl_status('test1')
9bacf431
DS
3918 print(output)
3919 self.assertRegex(output, '192.168.0.15')
3920 self.assertRegex(output, '192.168.0.1')
3921 self.assertRegex(output, routable_map[carrier])
e40a58b5 3922
9bacf431 3923 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
a962d857 3924 copy_network_unit('11-dummy.netdev')
9bacf431
DS
3925 start_networkd()
3926 self.wait_operstate('test1', 'off', '')
3927 check_output('ip link set dev test1 up carrier off')
3928
a962d857 3929 copy_network_unit('25-test1.network')
9bacf431 3930 restart_networkd()
95e1fbba 3931 self.wait_online('test1:no-carrier')
9bacf431
DS
3932
3933 carrier_map = {'on': '1', 'off': '0'}
3934 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3935 for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
3936 with self.subTest(carrier=carrier, have_config=have_config):
3937 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3938 check_output(f'ip link set dev test1 carrier {carrier}')
95e1fbba 3939 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
9bacf431 3940
10d670a3 3941 output = networkctl_status('test1')
9bacf431
DS
3942 print(output)
3943 if have_config:
3944 self.assertRegex(output, '192.168.0.15')
3945 self.assertRegex(output, '192.168.0.1')
3946 else:
3947 self.assertNotRegex(output, '192.168.0.15')
3948 self.assertNotRegex(output, '192.168.0.1')
3949 self.assertRegex(output, routable_map[carrier])
1f0e3109 3950
49454d9c 3951 def check_routing_policy_rule_test1(self):
462be8c9
YW
3952 print('### Checking routing policy rules requested by test1')
3953
65c24cd0 3954 output = check_output('ip rule list iif test1 priority 111')
1f0e3109 3955 print(output)
49454d9c 3956 self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
1f0e3109 3957
49454d9c 3958 output = check_output('ip -6 rule list iif test1 priority 100')
65c24cd0 3959 print(output)
49454d9c 3960 self.assertIn('100: from all iif test1 lookup 8', output)
65c24cd0 3961
49454d9c 3962 output = check_output('ip rule list iif test1 priority 101')
65c24cd0 3963 print(output)
49454d9c 3964 self.assertIn('101: from all iif test1 lookup 9', output)
65c24cd0 3965
5dc74c66
YW
3966 output = check_output('ip -6 rule list iif test1 priority 101')
3967 print(output)
3968 self.assertIn('101: from all iif test1 lookup 9', output)
3969
2e8a32af
HV
3970 output = check_output('ip rule list iif test1 priority 102')
3971 print(output)
49454d9c 3972 self.assertIn('102: from 0.0.0.0/8 iif test1 lookup 10', output)
2e8a32af 3973
49454d9c
YW
3974 output = check_output('ip rule list iif test1 priority 103')
3975 print(output)
3976 self.assertIn('103: from 10.0.0.0/16 iif test1 lookup 11 goto 111', output)
3977
3978 output = check_output('ip rule list iif test1 priority 104')
3979 print(output)
3980 self.assertIn('104: from 10.1.0.0/16 iif test1 lookup 12 nop', output)
3981
ec65c29e
BG
3982 output = check_output('ip rule list iif test1 priority 200')
3983 print(output)
3984 self.assertIn('200: from all fwmark 0/0x1 iif test1 lookup 20', output)
3985
3986 output = check_output('ip rule list iif test1 priority 201')
3987 print(output)
3988 self.assertIn('201: from all fwmark 0x7/0xff iif test1 lookup 21', output)
3989
3990 output = check_output('ip rule list iif test1 priority 202')
3991 print(output)
3992 self.assertIn('202: from all fwmark 0x270f iif test1 lookup 22', output)
3993
a10172b0
YW
3994 output = check_output('ip rule list to 192.0.2.0/26')
3995 print(output)
3996 self.assertIn('to 192.0.2.0/26 lookup 1001', output)
3997
3998 output = check_output('ip rule list to 192.0.2.64/26')
3999 print(output)
4000 self.assertIn('to 192.0.2.64/26 lookup 1001', output)
4001
4002 output = check_output('ip rule list to 192.0.2.128/26')
4003 print(output)
4004 self.assertIn('to 192.0.2.128/26 lookup 1001', output)
4005
4006 output = check_output('ip rule list to 192.0.2.192/26')
4007 print(output)
4008 self.assertIn('to 192.0.2.192/26 lookup 1001', output)
4009
49454d9c 4010 def check_routing_policy_rule_dummy98(self):
462be8c9
YW
4011 print('### Checking routing policy rules requested by dummy98')
4012
4013 output = check_output('ip rule list priority 112')
49454d9c
YW
4014 print(output)
4015 self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2e8a32af 4016
08581856
YW
4017 def _test_routing_policy_rule(self, manage_foreign_routes):
4018 if not manage_foreign_routes:
4019 copy_networkd_conf_dropin('networkd-manage-foreign-rules-no.conf')
49454d9c 4020 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
08581856
YW
4021
4022 stop_networkd()
4023
4024 check_output('ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16')
4025 check_output('ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64')
4026
49454d9c
YW
4027 start_networkd()
4028 self.wait_online('test1:degraded')
4029
4030 self.check_routing_policy_rule_test1()
10d670a3 4031 check_json(networkctl_json())
146726b2 4032
08581856
YW
4033 output = check_output('ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16')
4034 print(output)
4035 if manage_foreign_routes:
4036 self.assertEqual(output, '')
4037 else:
4038 self.assertIn(output, '20001: from 10.10.0.0/16 lookup 9999')
4039 check_output('ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16')
4040
4041 output = check_output('ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64')
4042 print(output)
4043 if manage_foreign_routes:
4044 self.assertEqual(output, '')
4045 else:
4046 self.assertIn(output, '20001: from 2001:db8:0:1::/64 lookup 9999')
4047 check_output('ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64')
4048
4049 def test_routing_policy_rule(self):
4050 first = True
4051 for manage_foreign_routes in [True, False]:
4052 if first:
4053 first = False
4054 else:
4055 self.tearDown()
4056
4057 print(f'### test_routing_policy_rule(manage_foreign_routes={manage_foreign_routes})')
4058 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4059 self._test_routing_policy_rule(manage_foreign_routes)
4060
cd2a1e2d 4061 def test_routing_policy_rule_restart_and_reconfigure(self):
a962d857
YW
4062 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
4063 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
b677774d 4064
cd2a1e2d
YW
4065 # For #11280 and #34068.
4066
a962d857
YW
4067 for trial in range(3):
4068 restart_networkd(show_logs=(trial > 0))
95e1fbba 4069 self.wait_online('test1:degraded', 'dummy98:degraded')
b677774d 4070
49454d9c
YW
4071 self.check_routing_policy_rule_test1()
4072 self.check_routing_policy_rule_dummy98()
b677774d 4073
cd2a1e2d
YW
4074 networkctl_reconfigure('test1')
4075 self.wait_online('test1:degraded')
4076
4077 self.check_routing_policy_rule_test1()
4078 self.check_routing_policy_rule_dummy98()
4079
4080 networkctl_reconfigure('dummy98')
4081 self.wait_online('dummy98:degraded')
4082
4083 self.check_routing_policy_rule_test1()
4084 self.check_routing_policy_rule_dummy98()
4085
87adeabf 4086 def test_routing_policy_rule_reconfigure(self):
a962d857 4087 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
87adeabf 4088 start_networkd()
95e1fbba 4089 self.wait_online('test1:degraded')
87adeabf
YW
4090
4091 output = check_output('ip rule list table 1011')
4092 print(output)
49ff3f34
YW
4093 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4094 self.assertIn('10112: from all oif test1 lookup 1011', output)
4095 self.assertIn('10113: from all iif test1 lookup 1011', output)
4096 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4097
4098 output = check_output('ip -6 rule list table 1011')
4099 print(output)
4100 self.assertIn('10112: from all oif test1 lookup 1011', output)
4101
a962d857
YW
4102 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
4103 networkctl_reload()
95e1fbba 4104 self.wait_online('test1:degraded')
49ff3f34
YW
4105
4106 output = check_output('ip rule list table 1011')
4107 print(output)
4108 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4109 self.assertIn('10112: from all oif test1 lookup 1011', output)
4110 self.assertIn('10113: from all iif test1 lookup 1011', output)
4111 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4112
4113 output = check_output('ip -6 rule list table 1011')
4114 print(output)
4115 self.assertNotIn('10112: from all oif test1 lookup 1011', output)
4116 self.assertIn('10113: from all iif test1 lookup 1011', output)
87adeabf 4117
a962d857
YW
4118 call('ip rule delete priority 10111')
4119 call('ip rule delete priority 10112')
4120 call('ip rule delete priority 10113')
4121 call('ip rule delete priority 10114')
4122 call('ip -6 rule delete priority 10113')
87adeabf
YW
4123
4124 output = check_output('ip rule list table 1011')
4125 print(output)
4126 self.assertEqual(output, '')
4127
49ff3f34
YW
4128 output = check_output('ip -6 rule list table 1011')
4129 print(output)
4130 self.assertEqual(output, '')
87adeabf 4131
a962d857 4132 networkctl_reconfigure('test1')
95e1fbba 4133 self.wait_online('test1:degraded')
87adeabf
YW
4134
4135 output = check_output('ip rule list table 1011')
4136 print(output)
49ff3f34
YW
4137 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4138 self.assertIn('10112: from all oif test1 lookup 1011', output)
4139 self.assertIn('10113: from all iif test1 lookup 1011', output)
4140 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4141
4142 output = check_output('ip -6 rule list table 1011')
4143 print(output)
4144 self.assertIn('10113: from all iif test1 lookup 1011', output)
87adeabf 4145
f7ae28fc
YW
4146 def test_routing_policy_rule_manual(self):
4147 # For issue #36244.
4148 copy_network_unit(
4149 '11-dummy.netdev',
4150 '25-routing-policy-rule-manual.network')
4151 start_networkd()
4152 self.wait_operstate('test1', operstate='off', setup_state='configuring', setup_timeout=20)
4153
4154 check_output('ip link add test2 type dummy')
4155 self.wait_operstate('test2', operstate='off', setup_state='configuring', setup_timeout=20)
4156
4157 networkctl('up', 'test2')
4158 self.wait_online('test2:degraded')
4159
4160 # The request for the routing policy rules are bound to test1. Hence, we need to wait for the rules
4161 # being configured explicitly.
4162 for _ in range(20):
4163 time.sleep(0.5)
4164
4165 output = check_output('ip -4 rule list table 51819')
4166 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4167 continue
4168
4169 output = check_output('ip -6 rule list table 51819')
4170 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4171 continue
4172
4173 output = check_output('ip -4 rule list table 51820')
4174 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4175 continue
4176
4177 output = check_output('ip -6 rule list table 51820')
4178 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4179 continue
4180
4181 break
4182 else:
4183 self.assertFalse(True)
4184
d586a2c3 4185 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
926062f0 4186 def test_routing_policy_rule_port_range(self):
a962d857 4187 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
2cf6fdff 4188 start_networkd()
95e1fbba 4189 self.wait_online('test1:degraded')
e40a58b5 4190
371810d1 4191 output = check_output('ip rule')
926062f0 4192 print(output)
24e37929
YW
4193 self.assertIn('111:', output)
4194 self.assertIn('from 192.168.100.18 ', output)
4195 self.assertIn('sport 1123-1150 ', output)
4196 self.assertIn('dport 3224-3290 ', output)
4197 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4198 self.assertIn('lookup 7 ', output)
1f0e3109 4199
d586a2c3 4200 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
efecf9cd 4201 def test_routing_policy_rule_invert(self):
a962d857 4202 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
2cf6fdff 4203 start_networkd()
95e1fbba 4204 self.wait_online('test1:degraded')
e40a58b5 4205
371810d1 4206 output = check_output('ip rule')
efecf9cd 4207 print(output)
24e37929
YW
4208 self.assertIn('111:', output)
4209 self.assertIn('not ', output)
4210 self.assertIn('from 192.168.100.18 ', output)
4211 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4212 self.assertIn('lookup 7 ', output)
efecf9cd 4213
4be1fc84
NC
4214 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
4215 def test_routing_policy_rule_l3mdev(self):
4216 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
4217 start_networkd()
95e1fbba 4218 self.wait_online('test1:degraded')
4be1fc84
NC
4219
4220 output = check_output('ip rule')
4221 print(output)
4222 self.assertIn('1500: from all lookup [l3mdev-table]', output)
4223 self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
4224
6be8e78e
YW
4225 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
4226 def test_routing_policy_rule_uidrange(self):
a962d857 4227 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
6be8e78e 4228 start_networkd()
95e1fbba 4229 self.wait_online('test1:degraded')
6be8e78e
YW
4230
4231 output = check_output('ip rule')
4232 print(output)
24e37929
YW
4233 self.assertIn('111:', output)
4234 self.assertIn('from 192.168.100.18 ', output)
4235 self.assertIn('lookup 7 ', output)
4236 self.assertIn('uidrange 100-200 ', output)
6be8e78e 4237
94dc76c9 4238 def _check_route_static(self, test1_is_managed: bool):
10d670a3 4239 output = networkctl_status('dummy98')
e2aea43f 4240 print(output)
94dc76c9
YW
4241 output = networkctl_status('test1')
4242 print(output)
0d34228f 4243
6d60f9db 4244 print('### ip -6 route show dev dummy98')
371810d1 4245 output = check_output('ip -6 route show dev dummy98')
0d34228f 4246 print(output)
0b5dc249
YW
4247 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4248 self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
180c5116 4249 self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
1f0e3109 4250
d9005dec
YW
4251 print('### ip -6 route show default')
4252 output = check_output('ip -6 route show default')
6d60f9db 4253 print(output)
0b5dc249
YW
4254 self.assertIn('default', output)
4255 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output)
1f0e3109 4256
6d60f9db 4257 print('### ip -4 route show dev dummy98')
371810d1 4258 output = check_output('ip -4 route show dev dummy98')
1f0e3109 4259 print(output)
0b5dc249
YW
4260 self.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output)
4261 self.assertIn('149.10.124.64 proto static scope link', output)
4262 self.assertIn('169.254.0.0/16 proto static scope link metric 2048', output)
902bbdc4
YW
4263 self.assertIn('192.168.1.1 proto static scope link initcwnd 20', output)
4264 self.assertIn('192.168.1.2 proto static scope link initrwnd 30', output)
4265 self.assertIn('192.168.1.3 proto static scope link advmss 30', output)
288f58c0 4266 self.assertIn('192.168.1.4 proto static scope link hoplimit 122', output)
0b5dc249 4267 self.assertIn('multicast 149.10.123.4 proto static', output)
1f0e3109 4268
6d60f9db 4269 print('### ip -4 route show dev dummy98 default')
371810d1 4270 output = check_output('ip -4 route show dev dummy98 default')
6d60f9db 4271 print(output)
0b5dc249
YW
4272 self.assertIn('default via 149.10.125.65 proto static onlink', output)
4273 self.assertIn('default via 149.10.124.64 proto static', output)
4274 self.assertIn('default proto static', output)
5e46ca98 4275 self.assertIn('default via 1.1.8.104 proto static', output)
1f0e3109 4276
6d60f9db
YW
4277 print('### ip -4 route show table local dev dummy98')
4278 output = check_output('ip -4 route show table local dev dummy98')
4279 print(output)
0b5dc249
YW
4280 self.assertIn('local 149.10.123.1 proto static scope host', output)
4281 self.assertIn('anycast 149.10.123.2 proto static scope link', output)
4282 self.assertIn('broadcast 149.10.123.3 proto static scope link', output)
6d60f9db 4283
b4f4f119
YW
4284 print('### ip -4 route show type blackhole')
4285 output = check_output('ip -4 route show type blackhole')
1f0e3109 4286 print(output)
0b5dc249 4287 self.assertIn('blackhole 202.54.1.2 proto static', output)
f5050e48 4288
b4f4f119
YW
4289 print('### ip -4 route show type unreachable')
4290 output = check_output('ip -4 route show type unreachable')
f5050e48 4291 print(output)
0b5dc249 4292 self.assertIn('unreachable 202.54.1.3 proto static', output)
f5050e48 4293
b4f4f119
YW
4294 print('### ip -4 route show type prohibit')
4295 output = check_output('ip -4 route show type prohibit')
f5050e48 4296 print(output)
0b5dc249 4297 self.assertIn('prohibit 202.54.1.4 proto static', output)
f5050e48 4298
452d86a5
YW
4299 print('### ip -6 route show type blackhole')
4300 output = check_output('ip -6 route show type blackhole')
4301 print(output)
4302 self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
4303
4304 print('### ip -6 route show type unreachable')
4305 output = check_output('ip -6 route show type unreachable')
4306 print(output)
4307 self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
4308
4309 print('### ip -6 route show type prohibit')
4310 output = check_output('ip -6 route show type prohibit')
4311 print(output)
4312 self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
4313
a0ce990e
YW
4314 print('### ip route show 192.168.10.1')
4315 output = check_output('ip route show 192.168.10.1')
4316 print(output)
0b5dc249 4317 self.assertIn('192.168.10.1 proto static', output)
d3779490
YW
4318 self.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output)
4319 self.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output)
0b5dc249
YW
4320 self.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output)
4321 self.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output)
a0ce990e
YW
4322
4323 print('### ip route show 192.168.10.2')
4324 output = check_output('ip route show 192.168.10.2')
4325 print(output)
4326 # old ip command does not show IPv6 gateways...
0b5dc249
YW
4327 self.assertIn('192.168.10.2 proto static', output)
4328 self.assertIn('nexthop', output)
d3779490
YW
4329 self.assertIn('dev test1 weight 20', output)
4330 self.assertIn('dev test1 weight 30', output)
0b5dc249
YW
4331 self.assertIn('dev dummy98 weight 10', output)
4332 self.assertIn('dev dummy98 weight 5', output)
a0ce990e 4333
cd650753
YW
4334 print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
4335 output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
a0ce990e
YW
4336 print(output)
4337 # old ip command does not show 'nexthop' keyword and weight...
cd650753 4338 self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output)
94dc76c9
YW
4339 if test1_is_managed:
4340 self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output)
4341 self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output)
0b5dc249
YW
4342 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
4343 self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
a0ce990e 4344
10d670a3 4345 check_json(networkctl_json())
146726b2 4346
94dc76c9 4347 def _check_unreachable_routes_removed(self):
b4f4f119
YW
4348 print('### ip -4 route show type blackhole')
4349 output = check_output('ip -4 route show type blackhole')
43d4bc9f
YW
4350 print(output)
4351 self.assertEqual(output, '')
4352
b4f4f119
YW
4353 print('### ip -4 route show type unreachable')
4354 output = check_output('ip -4 route show type unreachable')
43d4bc9f
YW
4355 print(output)
4356 self.assertEqual(output, '')
4357
b4f4f119
YW
4358 print('### ip -4 route show type prohibit')
4359 output = check_output('ip -4 route show type prohibit')
43d4bc9f
YW
4360 print(output)
4361 self.assertEqual(output, '')
4362
452d86a5
YW
4363 print('### ip -6 route show type blackhole')
4364 output = check_output('ip -6 route show type blackhole')
4365 print(output)
4366 self.assertEqual(output, '')
4367
4368 print('### ip -6 route show type unreachable')
4369 output = check_output('ip -6 route show type unreachable')
4370 print(output)
4371 self.assertEqual(output, '')
4372
4373 print('### ip -6 route show type prohibit')
4374 output = check_output('ip -6 route show type prohibit')
4375 print(output)
4376 self.assertEqual(output, '')
4377
94dc76c9 4378 check_json(networkctl_json())
43d4bc9f 4379
94dc76c9
YW
4380 def _test_route_static(self, manage_foreign_routes):
4381 if not manage_foreign_routes:
4382 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
43d4bc9f 4383
94dc76c9
YW
4384 copy_network_unit('25-route-static.network', '12-dummy.netdev',
4385 '25-route-static-test1.network', '11-dummy.netdev')
4386 start_networkd()
4387 self.wait_online('dummy98:routable', 'test1:routable')
4388 self._check_route_static(test1_is_managed=True)
43d4bc9f 4389
94dc76c9
YW
4390 # unmanaging test1
4391 remove_network_unit('25-route-static-test1.network')
4392 networkctl_reload()
4393 self.wait_online('dummy98:routable')
4394 self.wait_operstate('test1', setup_state='unmanaged')
4395 self._check_route_static(test1_is_managed=False)
43d4bc9f 4396
94dc76c9
YW
4397 # managing test1 again
4398 copy_network_unit('25-route-static-test1.network')
4399 networkctl_reload()
4400 self.wait_online('dummy98:routable', 'test1:routable')
4401 self._check_route_static(test1_is_managed=True)
452d86a5 4402
94dc76c9
YW
4403 # adding an unmanaged interface
4404 check_output('ip link add test2 type dummy')
4405 self.wait_operstate('test2', operstate='off', setup_state='unmanaged')
4406 self._check_route_static(test1_is_managed=True)
452d86a5 4407
94dc76c9
YW
4408 # reconfiguring with another config, and check all routes managed by Manager are removed
4409 copy_network_unit('25-address-static.network', copy_dropins=False)
4410 networkctl_reload()
4411 self.wait_online('dummy98:routable')
4412 self._check_unreachable_routes_removed()
452d86a5 4413
94dc76c9
YW
4414 # reconfiguring the original config again
4415 remove_network_unit('25-address-static.network')
4416 networkctl_reload()
4417 self.wait_online('dummy98:routable')
4418 self._check_route_static(test1_is_managed=True)
4419
4420 # removing interface, and check all routes managed by Manager are removed
a962d857 4421 remove_link('dummy98')
43d4bc9f 4422 time.sleep(2)
94dc76c9 4423 self._check_unreachable_routes_removed()
452d86a5 4424
1d26d4cd 4425 def test_route_static(self):
a962d857 4426 first = True
1d26d4cd 4427 for manage_foreign_routes in [True, False]:
a962d857
YW
4428 if first:
4429 first = False
4430 else:
4431 self.tearDown()
4432
4433 print(f'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
1d26d4cd
YW
4434 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4435 self._test_route_static(manage_foreign_routes)
4436
7f1b36a8
YW
4437 def test_route_static_issue_35047(self):
4438 copy_network_unit(
4439 '25-route-static-issue-35047.network',
4440 '25-route-static-issue-35047.network.d/step1.conf',
4441 '12-dummy.netdev',
4442 copy_dropins=False)
4443 start_networkd()
4444 self.wait_online('dummy98:routable')
4445
4446 print('### ip -4 route show table all dev dummy98')
4447 output = check_output('ip -4 route show table all dev dummy98')
4448 print(output)
4449 self.assertIn('192.0.2.2 proto kernel scope link src 192.0.2.1', output)
4450 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4451 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4452
4453 check_output('ip link set dev dummy98 down')
4454 self.wait_route_dropped('dummy98', '192.0.2.2 proto kernel scope link src 192.0.2.1', ipv='-4', table='all', timeout_sec=10)
4455 self.wait_route_dropped('dummy98', 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', ipv='-4', table='all', timeout_sec=10)
4456 self.wait_route_dropped('dummy98', '198.51.100.0/24 via 192.0.2.2 proto static', ipv='-4', table='all', timeout_sec=10)
4457
4458 print('### ip -4 route show table all dev dummy98')
4459 output = check_output('ip -4 route show table all dev dummy98')
4460 print(output)
4461 self.assertNotIn('192.0.2.2', output)
4462 self.assertNotIn('192.0.2.1', output)
4463 self.assertNotIn('198.51.100.0/24', output)
4464
4465 remove_network_unit('25-route-static-issue-35047.network.d/step1.conf')
4466 copy_network_unit('25-route-static-issue-35047.network.d/step2.conf')
4467 networkctl_reload()
4468 self.wait_online('dummy98:routable')
4469
4470 print('### ip -4 route show table all dev dummy98')
4471 output = check_output('ip -4 route show table all dev dummy98')
4472 print(output)
4473 self.assertIn('192.0.2.2 proto static scope link', output)
4474 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4475 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4476
ce5a54ed
YW
4477 def test_route_static_issue_37714(self):
4478 copy_network_unit('12-dummy.netdev', '25-route-static-issue-37714.network')
4479 start_networkd()
4480 self.wait_online('dummy98:routable')
4481
4482 print('### ip -4 rule list table 249')
4483 output = check_output('ip -4 rule list table 249')
4484 print(output)
4485 self.assertIn('32765: from 192.168.0.227 lookup 249 proto static', output)
4486
4487 print('### ip -6 rule list table 249')
4488 output = check_output('ip -6 rule list table 249')
4489 print(output)
4490 self.assertIn('32765: from 2000:f00::227 lookup 249 proto static', output)
4491
4492 print('### ip -4 route show table all dev dummy98')
4493 output = check_output('ip -4 route show table all dev dummy98')
4494 print(output)
4495 self.assertIn('default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink', output)
4496 self.assertIn('192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128', output)
4497 self.assertIn('10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4498 self.assertIn('192.168.0.72 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4499 self.assertIn('192.168.0.193 proto static scope link src 192.168.0.227 metric 128', output)
4500 self.assertIn('local 192.168.0.227 table local proto kernel scope host src 192.168.0.227', output)
4501 self.assertIn('broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227', output)
4502
4503 print('### ip -6 route show table all dev dummy98')
4504 output = check_output('ip -6 route show table all dev dummy98')
4505 print(output)
4506 self.assertIn('2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium', output)
4507 self.assertIn('default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium', output)
4508 self.assertIn('fe80::/64 proto kernel metric 256 pref medium', output)
4509 self.assertIn('local 2000:f00::227 table local proto kernel metric 0 pref medium', output)
4510 self.assertRegex(output, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium', output)
4511 self.assertIn('multicast ff00::/8 table local proto kernel metric 256 pref medium', output)
4512
297f9d86
YW
4513 @expectedFailureIfRTA_VIAIsNotSupported()
4514 def test_route_via_ipv6(self):
a962d857 4515 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
297f9d86 4516 start_networkd()
95e1fbba 4517 self.wait_online('dummy98:routable')
297f9d86 4518
10d670a3 4519 output = networkctl_status('dummy98')
297f9d86
YW
4520 print(output)
4521
4522 print('### ip -6 route show dev dummy98')
4523 output = check_output('ip -6 route show dev dummy98')
4524 print(output)
4525 self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
4526 self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
4527
4528 print('### ip -4 route show dev dummy98')
4529 output = check_output('ip -4 route show dev dummy98')
4530 print(output)
4531 self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
4532 self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
4533
93e898d6
YW
4534 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
4535 def test_route_congctl(self):
4536 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
4537 start_networkd()
95e1fbba 4538 self.wait_online('dummy98:routable')
93e898d6
YW
4539
4540 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4541 output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4542 print(output)
4543 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4544 self.assertIn('congctl dctcp', output)
4545
4546 print('### ip -4 route show dev dummy98 149.10.124.66')
4547 output = check_output('ip -4 route show dev dummy98 149.10.124.66')
4548 print(output)
4549 self.assertIn('149.10.124.66 proto static', output)
4550 self.assertIn('congctl dctcp', output)
1791956e 4551 self.assertIn('rto_min 300s', output)
93e898d6 4552
40afe491
YW
4553 @expectedFailureIfModuleIsNotAvailable('vrf')
4554 def test_route_vrf(self):
a962d857
YW
4555 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
4556 '25-vrf.netdev', '25-vrf.network')
40afe491 4557 start_networkd()
95e1fbba 4558 self.wait_online('dummy98:routable', 'vrf99:carrier')
40afe491
YW
4559
4560 output = check_output('ip route show vrf vrf99')
4561 print(output)
4562 self.assertRegex(output, 'default via 192.168.100.1')
4563
4564 output = check_output('ip route show')
4565 print(output)
4566 self.assertNotRegex(output, 'default via 192.168.100.1')
4567
0b1cd3e2 4568 def test_gateway_reconfigure(self):
a962d857 4569 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
0b1cd3e2 4570 start_networkd()
95e1fbba 4571 self.wait_online('dummy98:routable')
0b1cd3e2
WKI
4572 print('### ip -4 route show dev dummy98 default')
4573 output = check_output('ip -4 route show dev dummy98 default')
4574 print(output)
a962d857
YW
4575 self.assertIn('default via 149.10.124.59 proto static', output)
4576 self.assertNotIn('149.10.124.60', output)
0b1cd3e2 4577
a962d857
YW
4578 remove_network_unit('25-gateway-static.network')
4579 copy_network_unit('25-gateway-next-static.network')
4580 networkctl_reload()
95e1fbba 4581 self.wait_online('dummy98:routable')
0b1cd3e2
WKI
4582 print('### ip -4 route show dev dummy98 default')
4583 output = check_output('ip -4 route show dev dummy98 default')
4584 print(output)
a962d857
YW
4585 self.assertNotIn('149.10.124.59', output)
4586 self.assertIn('default via 149.10.124.60 proto static', output)
0b1cd3e2 4587
20ca06a6
DA
4588 def test_ip_route_ipv6_src_route(self):
4589 # a dummy device does not make the addresses go through tentative state, so we
4590 # reuse a bond from an earlier test, which does make the addresses go through
4591 # tentative state, and do our test on that
a962d857 4592 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 4593 start_networkd()
95e1fbba 4594 self.wait_online('dummy98:enslaved', 'bond199:routable')
20ca06a6 4595
371810d1 4596 output = check_output('ip -6 route list dev bond199')
20ca06a6 4597 print(output)
7e305278 4598 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output)
20ca06a6 4599
e4948bb2
YW
4600 def test_route_preferred_source_with_existing_address(self):
4601 # See issue #28009.
4602 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
4603 start_networkd()
4604
4605 for i in range(3):
4606 if i != 0:
4607 networkctl_reconfigure('dummy98')
4608
95e1fbba 4609 self.wait_online('dummy98:routable')
e4948bb2
YW
4610
4611 output = check_output('ip -6 route list dev dummy98')
4612 print(output)
4613 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output)
4614
f320c077
YW
4615 output = check_output('ip -4 route list dev dummy98')
4616 print(output)
4617 self.assertIn('10.123.0.0/16 via 192.168.30.1 proto static src 10.10.10.1', output)
4618
1f0e3109 4619 def test_ip_link_mac_address(self):
a962d857 4620 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
2cf6fdff 4621 start_networkd()
95e1fbba 4622 self.wait_online('dummy98:degraded')
1f0e3109 4623
371810d1 4624 output = check_output('ip link show dummy98')
1f0e3109 4625 print(output)
45aa0e84 4626 self.assertRegex(output, '00:01:02:aa:bb:cc')
1f0e3109
SS
4627
4628 def test_ip_link_unmanaged(self):
a962d857
YW
4629 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
4630 start_networkd()
1f0e3109 4631
19cf3143 4632 self.wait_operstate('dummy98', 'off', setup_state='unmanaged')
1f0e3109
SS
4633
4634 def test_ipv6_address_label(self):
a962d857 4635 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
489671d2 4636 copy_networkd_conf_dropin('networkd-address-label.conf')
2cf6fdff 4637 start_networkd()
95e1fbba 4638 self.wait_online('dummy98:degraded')
1f0e3109 4639
371810d1 4640 output = check_output('ip addrlabel list')
1f0e3109 4641 print(output)
489671d2
YW
4642 self.assertRegex(output, '2004:da8:1::/64 dev dummy98 label 4444')
4643 self.assertRegex(output, '2004:da8:2::/64 label 5555')
1f0e3109 4644
cff0cadc 4645 def test_ipv6_proxy_ndp(self):
a962d857 4646 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
cff0cadc
YW
4647 start_networkd()
4648
95e1fbba 4649 self.wait_online('dummy98:routable')
cff0cadc
YW
4650
4651 output = check_output('ip neighbor show proxy dev dummy98')
4652 print(output)
a962d857 4653 for i in range(1, 5):
cff0cadc
YW
4654 self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
4655
d4c8de21
MM
4656 def test_ipv6_neigh_retrans_time(self):
4657 link='test25'
4658 copy_network_unit('25-dummy.netdev', '25-dummy.network')
4659 start_networkd()
4660
95e1fbba 4661 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4662 remove_network_unit('25-dummy.network')
4663
4664 # expect retrans_time_ms updated
4665 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4666 networkctl_reload()
95e1fbba 4667 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4668 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4669 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4670
4671 # expect retrans_time_ms unchanged
4672 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4673 networkctl_reload()
95e1fbba 4674 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4675 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4676 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4677
4678 # expect retrans_time_ms unchanged
4679 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4680 networkctl_reload()
95e1fbba 4681 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4682 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4683 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4684
4685 # expect retrans_time_ms unchanged
4686 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4687 networkctl_reload()
95e1fbba 4688 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4689 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4690 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4691
4692 # expect retrans_time_ms unchanged
4693 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4694 networkctl_reload()
95e1fbba 4695 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4696 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4697 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4698
4699 # expect retrans_time_ms updated
4700 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4701 networkctl_reload()
95e1fbba 4702 self.wait_online(f'{link}:degraded')
d4c8de21
MM
4703 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000')
4704 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4705
9362f7d5
YW
4706 def test_neighbor(self):
4707 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
4708 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
4709 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
4710 copy_dropins=False)
2cf6fdff 4711 start_networkd()
95e1fbba 4712 self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
9362f7d5
YW
4713
4714 print('### ip neigh list dev gretun97')
4715 output = check_output('ip neigh list dev gretun97')
4716 print(output)
4717 self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
4718 self.assertNotIn('10.0.0.23', output)
4719
4720 print('### ip neigh list dev ip6gretun97')
4721 output = check_output('ip neigh list dev ip6gretun97')
4722 print(output)
4723 self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
4724 self.assertNotIn('2001:db8:0:f102::18', output)
e4a71bf3 4725
d1bdafd2 4726 print('### ip neigh list dev dummy98')
df7f9afa 4727 output = check_output('ip neigh list dev dummy98')
e4a71bf3 4728 print(output)
2ede3559
YW
4729 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
4730 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
4731 self.assertNotIn('2004:da8:1:0::2', output)
4732 self.assertNotIn('192.168.10.2', output)
4733 self.assertNotIn('00:00:5e:00:02:67', output)
e4a71bf3 4734
10d670a3 4735 check_json(networkctl_json())
146726b2 4736
9362f7d5
YW
4737 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
4738 # the valid configurations in 10-step1.conf.
4739 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
2ede3559 4740 networkctl_reload()
95e1fbba 4741 self.wait_online('dummy98:degraded')
2ede3559 4742
9362f7d5 4743 print('### ip neigh list dev dummy98')
2ede3559
YW
4744 output = check_output('ip neigh list dev dummy98')
4745 print(output)
4746 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
4747 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
4748 self.assertNotIn('2004:da8:1:0::2', output)
4749 self.assertNotIn('192.168.10.2', output)
9362f7d5 4750 self.assertNotIn('00:00:5e:00:02:67', output)
d1bdafd2 4751
10d670a3 4752 check_json(networkctl_json())
d1bdafd2 4753
9362f7d5
YW
4754 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
4755 '25-neighbor-dummy.network.d/10-step2.conf')
4756 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
a962d857 4757 networkctl_reload()
95e1fbba 4758 self.wait_online('dummy98:degraded')
9362f7d5 4759
d1bdafd2
WKI
4760 print('### ip neigh list dev dummy98')
4761 output = check_output('ip neigh list dev dummy98')
4762 print(output)
9362f7d5 4763 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
2ede3559 4764 self.assertNotIn('00:00:5e:00:02:65', output)
9362f7d5
YW
4765 self.assertNotIn('00:00:5e:00:02:66', output)
4766 self.assertNotIn('00:00:5e:00:03:65', output)
2ede3559 4767 self.assertNotIn('2004:da8:1::1', output)
d1bdafd2 4768
05514ae1 4769 def test_link_local_addressing(self):
a962d857
YW
4770 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
4771 '25-link-local-addressing-no.network', '12-dummy.netdev')
2cf6fdff 4772 start_networkd()
95e1fbba 4773 self.wait_online('test1:degraded', 'dummy98:carrier')
05514ae1 4774
371810d1 4775 output = check_output('ip address show dev test1')
05514ae1
YW
4776 print(output)
4777 self.assertRegex(output, 'inet .* scope link')
4778 self.assertRegex(output, 'inet6 .* scope link')
4779
371810d1 4780 output = check_output('ip address show dev dummy98')
05514ae1
YW
4781 print(output)
4782 self.assertNotRegex(output, 'inet6* .* scope link')
4783
f7805a6c
FS
4784 # Documentation/networking/ip-sysctl.txt
4785 #
4786 # addr_gen_mode - INTEGER
4787 # Defines how link-local and autoconf addresses are generated.
4788 #
4789 # 0: generate address based on EUI64 (default)
4790 # 1: do no generate a link-local address, use EUI64 for addresses generated
4791 # from autoconf
4792 # 2: generate stable privacy addresses, using the secret from
4793 # stable_secret (RFC7217)
4794 # 3: generate stable privacy addresses, using a random secret if unset
05514ae1 4795
a962d857
YW
4796 self.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
4797 self.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
4798 self.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
05514ae1 4799
2becdbcc 4800 def test_link_local_addressing_ipv6ll(self):
a962d857 4801 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
7b3770a7 4802 start_networkd()
95e1fbba 4803 self.wait_online('dummy98:degraded')
7b3770a7 4804
2becdbcc 4805 # An IPv6LL address exists by default.
7b3770a7
YW
4806 output = check_output('ip address show dev dummy98')
4807 print(output)
4808 self.assertRegex(output, 'inet6 .* scope link')
4809
a962d857
YW
4810 copy_network_unit('25-link-local-addressing-no.network')
4811 networkctl_reload()
95e1fbba 4812 self.wait_online('dummy98:carrier')
7b3770a7 4813
2becdbcc 4814 # Check if the IPv6LL address is removed.
7b3770a7
YW
4815 output = check_output('ip address show dev dummy98')
4816 print(output)
2becdbcc
YW
4817 self.assertNotRegex(output, 'inet6 .* scope link')
4818
a962d857
YW
4819 remove_network_unit('25-link-local-addressing-no.network')
4820 networkctl_reload()
95e1fbba 4821 self.wait_online('dummy98:degraded')
2becdbcc
YW
4822
4823 # Check if a new IPv6LL address is assigned.
4824 output = check_output('ip address show dev dummy98')
4825 print(output)
4826 self.assertRegex(output, 'inet6 .* scope link')
7b3770a7 4827
1f0e3109 4828 def test_sysctl(self):
856a247e
YW
4829 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
4830 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
2cf6fdff 4831 start_networkd()
95e1fbba 4832 self.wait_online('dummy98:degraded')
ec38833c 4833
a962d857 4834 self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
856a247e 4835 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
a962d857
YW
4836 self.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
4837 self.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
4838 self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
4839 self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
4840 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
b4959550 4841 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
a962d857 4842 self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
ab2d9e29 4843 self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
d865abf9 4844 self.check_ipv4_sysctl_attr('dummy98', 'force_igmp_version', '1')
1f0e3109 4845
856a247e
YW
4846 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
4847 networkctl_reload()
95e1fbba 4848 self.wait_online('dummy98:degraded')
856a247e
YW
4849
4850 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
4851
4da33154 4852 def test_sysctl_disable_ipv6(self):
a962d857 4853 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
4da33154
YW
4854
4855 print('## Disable ipv6')
cefd6b3d
ZJS
4856 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
4857 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
4da33154 4858
2cf6fdff 4859 start_networkd()
95e1fbba 4860 self.wait_online('dummy98:routable')
4da33154 4861
371810d1 4862 output = check_output('ip -4 address show dummy98')
4da33154
YW
4863 print(output)
4864 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
371810d1 4865 output = check_output('ip -6 address show dummy98')
4da33154 4866 print(output)
57ad7607
ZJS
4867 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4868 self.assertRegex(output, 'inet6 .* scope link')
4933b97d
YW
4869 output = check_output('ip -4 route show dev dummy98')
4870 print(output)
3d2c2692 4871 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
d9005dec 4872 output = check_output('ip -6 route show default')
4933b97d 4873 print(output)
d9005dec
YW
4874 self.assertRegex(output, 'default')
4875 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4da33154 4876
a962d857 4877 remove_link('dummy98')
4da33154
YW
4878
4879 print('## Enable ipv6')
cefd6b3d
ZJS
4880 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
4881 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
4da33154 4882
a962d857 4883 restart_networkd()
95e1fbba 4884 self.wait_online('dummy98:routable')
4da33154 4885
371810d1 4886 output = check_output('ip -4 address show dummy98')
4da33154
YW
4887 print(output)
4888 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
371810d1 4889 output = check_output('ip -6 address show dummy98')
4da33154 4890 print(output)
4933b97d 4891 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4da33154 4892 self.assertRegex(output, 'inet6 .* scope link')
4933b97d
YW
4893 output = check_output('ip -4 route show dev dummy98')
4894 print(output)
3d2c2692 4895 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
d9005dec 4896 output = check_output('ip -6 route show default')
4933b97d 4897 print(output)
d9005dec 4898 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4da33154 4899
af44a16e
YW
4900 @expectedFailureIfModuleIsNotAvailable('mpls_router')
4901 def test_sysctl_mpls(self):
4902 check_output('modprobe mpls_router')
4903 copy_network_unit('25-sysctl-mpls.network', '12-dummy.netdev')
4904 start_networkd()
4905 self.wait_online('dummy98:degraded')
4906
4907 self.check_mpls_sysctl_attr('dummy98', 'input', '1')
4908
cd65d067 4909 def test_bind_carrier(self):
a962d857 4910 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
2cf6fdff 4911 start_networkd()
cd65d067 4912
3e2f7c46
YW
4913 # no bound interface.
4914 self.wait_operstate('test1', 'off', setup_state='configuring')
371810d1 4915 output = check_output('ip address show test1')
cd65d067 4916 print(output)
3e2f7c46
YW
4917 self.assertNotIn('UP,LOWER_UP', output)
4918 self.assertIn('DOWN', output)
4919 self.assertNotIn('192.168.10', output)
cd65d067 4920
3e2f7c46
YW
4921 # add one bound interface. The interface will be up.
4922 check_output('ip link add dummy98 type dummy')
4923 check_output('ip link set dummy98 up')
95e1fbba 4924 self.wait_online('test1:routable')
3e2f7c46
YW
4925 output = check_output('ip address show test1')
4926 print(output)
4927 self.assertIn('UP,LOWER_UP', output)
4928 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4929
4930 # add another bound interface. The interface is still up.
cefd6b3d
ZJS
4931 check_output('ip link add dummy99 type dummy')
4932 check_output('ip link set dummy99 up')
fcd79988 4933 self.wait_operstate('dummy99', 'degraded', setup_state='unmanaged')
371810d1 4934 output = check_output('ip address show test1')
cd65d067 4935 print(output)
3e2f7c46
YW
4936 self.assertIn('UP,LOWER_UP', output)
4937 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 4938
3e2f7c46 4939 # remove one of the bound interfaces. The interface is still up
a962d857 4940 remove_link('dummy98')
371810d1 4941 output = check_output('ip address show test1')
cd65d067 4942 print(output)
3e2f7c46
YW
4943 self.assertIn('UP,LOWER_UP', output)
4944 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 4945
3e2f7c46 4946 # bring down the remaining bound interface. The interface will be down.
bc942f69 4947 check_output('ip link set dummy99 down')
3e2f7c46
YW
4948 self.wait_operstate('test1', 'off')
4949 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
371810d1 4950 output = check_output('ip address show test1')
cd65d067 4951 print(output)
3e2f7c46
YW
4952 self.assertNotIn('UP,LOWER_UP', output)
4953 self.assertIn('DOWN', output)
4954 self.assertNotIn('192.168.10', output)
cd65d067 4955
3e2f7c46 4956 # bring up the bound interface. The interface will be up.
bc942f69 4957 check_output('ip link set dummy99 up')
95e1fbba 4958 self.wait_online('test1:routable')
371810d1 4959 output = check_output('ip address show test1')
cd65d067 4960 print(output)
3e2f7c46
YW
4961 self.assertIn('UP,LOWER_UP', output)
4962 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4963
4964 # remove the remaining bound interface. The interface will be down.
4965 remove_link('dummy99')
4966 self.wait_operstate('test1', 'off')
4967 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4968 output = check_output('ip address show test1')
4969 print(output)
4970 self.assertNotIn('UP,LOWER_UP', output)
4971 self.assertIn('DOWN', output)
4972 self.assertNotIn('192.168.10', output)
4973
4974 # re-add one bound interface. The interface will be up.
4975 check_output('ip link add dummy98 type dummy')
4976 check_output('ip link set dummy98 up')
95e1fbba 4977 self.wait_online('test1:routable')
3e2f7c46
YW
4978 output = check_output('ip address show test1')
4979 print(output)
4980 self.assertIn('UP,LOWER_UP', output)
4981 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
cd65d067 4982
a962d857 4983 def _test_activation_policy(self, interface, test):
2236d75d
DS
4984 conffile = '25-activation-policy.network'
4985 if test:
4986 conffile = f'{conffile}.d/{test}.conf'
ee9918ae 4987 if interface == 'vlan99':
a962d857
YW
4988 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
4989 copy_network_unit('11-dummy.netdev', conffile, copy_dropins=False)
2236d75d
DS
4990 start_networkd()
4991
4992 always = test.startswith('always')
ebb5036f 4993 initial_up = test != 'manual' and not test.endswith('down') # note: default is up
2236d75d
DS
4994 expect_up = initial_up
4995 next_up = not expect_up
4996
cfbdc438 4997 if test.endswith('down'):
ee9918ae 4998 self.wait_activated(interface)
cfbdc438 4999
2236d75d
DS
5000 for iteration in range(4):
5001 with self.subTest(iteration=iteration, expect_up=expect_up):
5002 operstate = 'routable' if expect_up else 'off'
618da3e7 5003 setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None)
ee9918ae 5004 self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20)
2236d75d
DS
5005
5006 if expect_up:
ee9918ae
YW
5007 self.assertIn('UP', check_output(f'ip link show {interface}'))
5008 self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}'))
5009 self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}'))
2236d75d 5010 else:
ee9918ae 5011 self.assertIn('DOWN', check_output(f'ip link show {interface}'))
2236d75d
DS
5012
5013 if next_up:
ee9918ae 5014 check_output(f'ip link set dev {interface} up')
2236d75d 5015 else:
ee9918ae 5016 check_output(f'ip link set dev {interface} down')
2236d75d
DS
5017 expect_up = initial_up if always else next_up
5018 next_up = not next_up
073ad7ed
YW
5019 if always:
5020 time.sleep(1)
2236d75d 5021
2236d75d 5022 def test_activation_policy(self):
a962d857 5023 first = True
ee9918ae 5024 for interface in ['test1', 'vlan99']:
a962d857
YW
5025 for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
5026 if first:
5027 first = False
5028 else:
5029 self.tearDown()
5030
5031 print(f'### test_activation_policy(interface={interface}, test={test})')
5032 with self.subTest(interface=interface, test=test):
5033 self._test_activation_policy(interface, test)
2236d75d 5034
61764fe4 5035 def _test_activation_policy_required_for_online(self, policy, required):
61764fe4
DS
5036 conffile = '25-activation-policy.network'
5037 units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
5038 if policy:
5039 units += [f'{conffile}.d/{policy}.conf']
5040 if required:
5041 units += [f'{conffile}.d/required-{required}.conf']
a962d857 5042 copy_network_unit(*units, copy_dropins=False)
61764fe4
DS
5043 start_networkd()
5044
cfbdc438
YW
5045 if policy.endswith('down'):
5046 self.wait_activated('test1')
5047
61764fe4
DS
5048 if policy.endswith('down') or policy == 'manual':
5049 self.wait_operstate('test1', 'off', setup_state='configuring')
5050 else:
95e1fbba 5051 self.wait_online('test1')
61764fe4
DS
5052
5053 if policy == 'always-down':
5054 # if always-down, required for online is forced to no
5055 expected = False
5056 elif required:
5057 # otherwise if required for online is specified, it should match that
5058 expected = required == 'yes'
5059 elif policy:
5060 # otherwise if only policy specified, required for online defaults to
5061 # true if policy is up, always-up, or bound
5062 expected = policy.endswith('up') or policy == 'bound'
5063 else:
5064 # default is true, if neither are specified
5065 expected = True
5066
10d670a3 5067 output = networkctl_status('test1')
61764fe4
DS
5068 print(output)
5069
5070 yesno = 'yes' if expected else 'no'
5071 self.assertRegex(output, f'Required For Online: {yesno}')
5072
61764fe4 5073 def test_activation_policy_required_for_online(self):
a962d857 5074 first = True
61764fe4
DS
5075 for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
5076 for required in ['yes', 'no', '']:
a962d857
YW
5077 if first:
5078 first = False
5079 else:
5080 self.tearDown()
5081
5082 print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})')
61764fe4
DS
5083 with self.subTest(policy=policy, required=required):
5084 self._test_activation_policy_required_for_online(policy, required)
5085
fdcd1ec5 5086 def test_domain(self):
a962d857 5087 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
2cf6fdff 5088 start_networkd()
95e1fbba 5089 self.wait_online('dummy98:routable')
fdcd1ec5 5090
10d670a3 5091 output = networkctl_status('dummy98')
fdcd1ec5
YW
5092 print(output)
5093 self.assertRegex(output, 'Address: 192.168.42.100')
5094 self.assertRegex(output, 'DNS: 192.168.42.1')
5095 self.assertRegex(output, 'Search Domains: one')
5096
1cac0676
YW
5097 def test_keep_configuration_yes(self):
5098 check_output('ip link add dummy98 type dummy')
5099 check_output('ip link set dev dummy98 up')
5100 check_output('ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98')
5101 check_output('ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot')
5102
5103 copy_network_unit('24-keep-configuration-yes.network')
5104 start_networkd()
5105 self.wait_online('dummy98:routable')
5106
5107 output = check_output('ip address show dummy98')
5108 print(output)
5109 self.assertIn('inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98', output)
5110
5111 output = check_output('ip -d -4 route show dev dummy98')
5112 print(output)
5113 self.assertIn('203.0.113.0/24 via 198.51.100.10 proto boot', output)
5114
1e498853 5115 def test_keep_configuration_static(self):
1e498853
YW
5116 check_output('ip link add name dummy98 type dummy')
5117 check_output('ip address add 10.1.2.3/16 dev dummy98')
5118 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
5119 output = check_output('ip address show dummy98')
5120 print(output)
5121 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5122 self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5123 output = check_output('ip route show dev dummy98')
5124 print(output)
5125
a962d857 5126 copy_network_unit('24-keep-configuration-static.network')
2cf6fdff 5127 start_networkd()
95e1fbba 5128 self.wait_online('dummy98:routable')
1e498853
YW
5129
5130 output = check_output('ip address show dummy98')
5131 print(output)
5132 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5133 self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5134
912a4857
MC
5135 def check_keep_configuration_on_restart(self):
5136 self.wait_online('dummy98:routable')
5137 self.wait_online('unmanaged0:routable', setup_state='unmanaged')
5138
5139 print('### ip -6 address show dev dummy98')
5140 output = check_output('ip -6 address show dev dummy98')
5141 print(output)
5142 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
5143 self.assertIn('inet6 2001:db8:1:f101::15/64 scope global deprecated', output)
5144
5145 print('### ip -6 address show dev unmanaged0')
5146 output = check_output('ip -6 address show dev unmanaged0')
5147 print(output)
5148 self.assertIn('inet6 2001:db8:9999:f101::15/64 scope global', output)
5149
5150 print('### ip -6 route show default dev dummy98')
5151 output = check_output('ip -6 route show default dev dummy98')
5152 print(output)
5153 self.assertIn('default via fe80::f0ca:cc1a proto static metric 1 pref medium', output)
5154
5155 def test_keep_configuration_on_restart(self):
bcb9e72b
MC
5156 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network', '85-unmanaged.link')
5157
912a4857
MC
5158 # Add an unmanaged interface with an up address
5159 call('ip link add unmanaged0 type dummy')
5160 call('ip link set unmanaged0 up')
bcb9e72b 5161 call('ip -4 addr add 10.20.30.40/32 dev unmanaged0')
912a4857
MC
5162 call('ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0')
5163
d63c8ce0
YW
5164 # Wait for all addresses
5165 self.wait_address('unmanaged0', 'inet 10.20.30.40/32', scope='global', ipv='-4', timeout_sec=10)
5166 self.wait_address('unmanaged0', 'inet6 2001:db8:9999:f101::15/64', scope='global', ipv='-6', timeout_sec=10)
5167 self.wait_address('unmanaged0', 'inet6 fe80::[0-9a-f:]*/64', scope='link', ipv='-6', timeout_sec=10)
5168
5169 # Wait for all routes
5170 self.wait_route('unmanaged0', 'local 10.20.30.40 proto kernel', table='local', ipv='-4', timeout_sec=10)
5171 self.wait_route('unmanaged0', 'local fe80::[0-9a-f:]* proto kernel', table='local', ipv='-6', timeout_sec=10)
5172 self.wait_route('unmanaged0', 'multicast ff00::/8 proto kernel', table='local', ipv='-6', timeout_sec=10)
5173 self.wait_route('unmanaged0', '2001:db8:9999:f101::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5174 self.wait_route('unmanaged0', 'fe80::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5175
912a4857 5176 # Start `ip monitor` with output to a temporary file
bcb9e72b
MC
5177 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor_u') as logfile_unmanaged:
5178 process_u = subprocess.Popen(['ip', 'monitor', 'dev', 'unmanaged0'], stdout=logfile_unmanaged, text=True)
5179
5180 start_networkd()
912a4857
MC
5181 self.check_keep_configuration_on_restart()
5182
bcb9e72b
MC
5183 # Start `ip monitor` with output to a temporary file
5184 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor') as logfile:
5185 process = subprocess.Popen(['ip', 'monitor', 'dev', 'dummy98'], stdout=logfile, text=True)
5186
5187 restart_networkd()
5188 self.check_keep_configuration_on_restart()
5189
5190 process.send_signal(signal.SIGTERM)
5191 process.wait()
5192
5193 print('### ip monitor dev dummy98 BEGIN')
912a4857 5194
bcb9e72b
MC
5195 # Read the `ip monitor` output looking for network changes
5196 logfile.seek(0)
5197 for line in logfile:
6288739e
MC
5198 line = line.rstrip()
5199 print(line)
bcb9e72b
MC
5200 # Check if a link went down
5201 self.assertNotRegex(line, 'dummy98: .* state DOWN')
5202 # Check if an address was removed
5203 self.assertNotRegex(line, '^Deleted .* 2001:db8:')
5204 self.assertNotRegex(line, '^Deleted 2001:db8:.*/64')
5205 # Check if the default route was removed
5206 self.assertNotRegex(line, '^Deleted default via fe80::f0ca:cc1a')
5207
5208 print('### ip monitor dev dummy98 END')
5209
5210 process_u.send_signal(signal.SIGTERM)
5211 process_u.wait()
5212
5213 print('### ip monitor dev unmanaged0 BEGIN')
912a4857 5214
d63c8ce0 5215 # Read the `ip monitor` output looking for network changes and check if something happened
bcb9e72b 5216 logfile_unmanaged.seek(0)
d63c8ce0 5217 self.assertEqual(logfile_unmanaged.read().rstrip(), '')
912a4857 5218
bcb9e72b 5219 print('### ip monitor dev unmanaged0 END')
912a4857 5220
6479204e
MC
5221 def test_keep_untracked_addresses(self):
5222 # Add an unmanaged interface with an up address
5223
5224 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network')
5225 start_networkd()
5226 self.wait_online('dummy98:routable')
5227
5228 print('### ip -4 addr add 10.234.77.111/32 dev dummy98')
5229 output = check_output('ip -4 addr add 10.234.77.111/32 dev dummy98')
5230 print(output)
5231
5232 print('### ip -6 addr add 2222:3333::4444/64 dev dummy98')
5233 output = check_output('ip -6 addr add 2222:3333::4444/64 dev dummy98')
5234 print(output)
5235
5236 restart_networkd()
5237
5238 output = check_output('ip -4 addr show dev dummy98')
5239 print(output)
ff6f0a58 5240 self.assertIn('inet 10.234.77.111/32', output)
6479204e
MC
5241
5242 output = check_output('ip -6 addr show dev dummy98')
5243 print(output)
ff6f0a58 5244 self.assertIn('inet6 2222:3333::4444/64 scope global', output)
6479204e 5245
78265b5b 5246 def check_nexthop(self, manage_foreign_nexthops, first):
95e1fbba 5247 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
086bcf5d 5248
e7660b9a
YW
5249 output = check_output('ip nexthop list dev veth99')
5250 print(output)
78265b5b
YW
5251 if first:
5252 self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
5253 self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
5254 else:
5255 self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
5256 self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output)
e7660b9a
YW
5257 self.assertIn('id 3 dev veth99', output)
5258 self.assertIn('id 4 dev veth99', output)
78265b5b
YW
5259 if first:
5260 self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
5261 else:
5262 self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
5263 self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
e7660b9a 5264 self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
41231f26
YW
5265 if manage_foreign_nexthops:
5266 self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
086bcf5d 5267
e7660b9a
YW
5268 output = check_output('ip nexthop list dev dummy98')
5269 print(output)
78265b5b
YW
5270 if first:
5271 self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
5272 else:
5273 self.assertIn('id 21 via 192.168.20.1 dev dummy98', output)
41231f26
YW
5274 if manage_foreign_nexthops:
5275 self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
5276 else:
5277 self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
69a91c70 5278
e7660b9a
YW
5279 # kernel manages blackhole nexthops on lo
5280 output = check_output('ip nexthop list dev lo')
5281 print(output)
78265b5b
YW
5282 if first:
5283 self.assertIn('id 6 blackhole', output)
5284 self.assertIn('id 7 blackhole', output)
5285 else:
5286 self.assertIn('id 1 blackhole', output)
5287 self.assertIn('id 2 blackhole', output)
cee0f719 5288
e7660b9a 5289 # group nexthops are shown with -0 option
78265b5b
YW
5290 if first:
5291 output = check_output('ip -0 nexthop list id 21')
5292 print(output)
5293 self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
5294 else:
5295 output = check_output('ip -0 nexthop list id 20')
5296 print(output)
5297 self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)')
cee0f719 5298
e7660b9a
YW
5299 output = check_output('ip route show dev veth99 10.10.10.10')
5300 print(output)
78265b5b
YW
5301 if first:
5302 self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
5303 else:
5304 self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output)
e2d9bc5c 5305
e7660b9a
YW
5306 output = check_output('ip route show dev veth99 10.10.10.11')
5307 print(output)
78265b5b
YW
5308 if first:
5309 self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
5310 else:
5311 self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output)
cee0f719 5312
e7660b9a
YW
5313 output = check_output('ip route show dev veth99 10.10.10.12')
5314 print(output)
78265b5b
YW
5315 if first:
5316 self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
5317 else:
5318 self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output)
69a91c70 5319
e7660b9a
YW
5320 output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
5321 print(output)
78265b5b
YW
5322 if first:
5323 self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
5324 else:
5325 self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
9c8f90d0 5326
e7660b9a
YW
5327 output = check_output('ip route show 10.10.10.13')
5328 print(output)
78265b5b
YW
5329 if first:
5330 self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
5331 else:
5332 self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output)
9c8f90d0 5333
e7660b9a
YW
5334 output = check_output('ip -6 route show 2001:1234:5:8f62::2')
5335 print(output)
78265b5b
YW
5336 if first:
5337 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
5338 else:
5339 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output)
9c8f90d0 5340
e7660b9a
YW
5341 output = check_output('ip route show 10.10.10.14')
5342 print(output)
78265b5b
YW
5343 if first:
5344 self.assertIn('10.10.10.14 nhid 21 proto static', output)
5345 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5346 else:
5347 self.assertIn('10.10.10.14 nhid 20 proto static', output)
5348 self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output)
e7660b9a 5349 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
146726b2 5350
b5edf3a9
YW
5351 output = networkctl_json()
5352 check_json(output)
5353 self.assertNotIn('"Destination":[10.10.10.14]', output)
e7660b9a 5354
41231f26
YW
5355 def _test_nexthop(self, manage_foreign_nexthops):
5356 if not manage_foreign_nexthops:
5357 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
5358
5359 check_output('ip link add dummy98 type dummy')
5360 check_output('ip link set dummy98 up')
5361 check_output('ip address add 192.168.20.20/24 dev dummy98')
5362 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
5363
78265b5b
YW
5364 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
5365 '12-dummy.netdev', '25-nexthop-dummy-1.network')
9c8f90d0
YW
5366 start_networkd()
5367
78265b5b
YW
5368 self.check_nexthop(manage_foreign_nexthops, first=True)
5369
5370 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
5371 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
5372 networkctl_reload()
5373 self.check_nexthop(manage_foreign_nexthops, first=False)
69a91c70 5374
78265b5b 5375 remove_network_unit('25-nexthop-2.network')
a962d857
YW
5376 copy_network_unit('25-nexthop-nothing.network')
5377 networkctl_reload()
95e1fbba 5378 self.wait_online('veth99:routable', 'veth-peer:routable')
932e157b 5379
9947c7ba
YW
5380 output = check_output('ip nexthop list dev veth99')
5381 print(output)
5382 self.assertEqual(output, '')
5383 output = check_output('ip nexthop list dev lo')
5384 print(output)
5385 self.assertEqual(output, '')
5386
78265b5b
YW
5387 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
5388 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
2ec0e95e
YW
5389 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
5390 # here to test reconfiguring with different .network files does not trigger race.
5391 # See also comments in link_drop_requests().
5392 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
5393 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
9947c7ba 5394
78265b5b 5395 self.check_nexthop(manage_foreign_nexthops, first=True)
932e157b 5396
f9b5c276
YW
5397 # Remove nexthop with ID 20
5398 check_output('ip nexthop del id 20')
1ca180b9
YW
5399
5400 # Check the nexthop ID 20 is dropped from the group nexthop.
5401 output = check_output('ip -0 nexthop list id 21')
5402 print(output)
5403 self.assertRegex(output, r'id 21 group 1,3')
5404
5405 # Remove nexthop with ID 21
5406 check_output('ip nexthop del id 21')
f9b5c276
YW
5407 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
5408 networkctl_reload()
5409
1ca180b9 5410 # 25-nexthop-test1.network requests a route with nexthop ID 21, which is removed in the above,
f9b5c276
YW
5411 # hence test1 should be stuck in the configuring state.
5412 self.wait_operstate('test1', operstate='routable', setup_state='configuring')
5413
5414 # Wait for a while, and check if the interface is still in the configuring state.
5415 time.sleep(1)
5416 output = networkctl_status('test1')
5417 self.assertIn('State: routable (configuring)', output)
5418
1ca180b9 5419 # Check if the route which needs nexthop 21 are forgotten.
b5edf3a9
YW
5420 output = networkctl_json()
5421 check_json(output)
5422 self.assertNotIn('"Destination":[10.10.10.14]', output)
5423
f9b5c276
YW
5424 # Reconfigure the interface that has nexthop with ID 20 and 21,
5425 # then the route requested by test1 can be configured.
5426 networkctl_reconfigure('dummy98')
95e1fbba 5427 self.wait_online('test1:routable')
f9b5c276
YW
5428
5429 # Check if the requested route actually configured.
5430 output = check_output('ip route show 10.10.11.10')
5431 print(output)
5432 self.assertIn('10.10.11.10 nhid 21 proto static', output)
5433 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5434 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
5435
a962d857 5436 remove_link('veth99')
9947c7ba
YW
5437 time.sleep(2)
5438
5439 output = check_output('ip nexthop list dev lo')
5440 print(output)
5441 self.assertEqual(output, '')
5442
41231f26
YW
5443 @expectedFailureIfNexthopIsNotAvailable()
5444 def test_nexthop(self):
5445 first = True
5446 for manage_foreign_nexthops in [True, False]:
5447 if first:
5448 first = False
5449 else:
5450 self.tearDown()
5451
5452 print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
5453 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
5454 self._test_nexthop(manage_foreign_nexthops)
5455
23b38192
YW
5456class NetworkdTCTests(unittest.TestCase, Utilities):
5457
5458 def setUp(self):
5459 setup_common()
5460
5461 def tearDown(self):
5462 tear_down_common()
5463
4c7d13f4
YW
5464 @expectedFailureIfModuleIsNotAvailable('sch_cake')
5465 def test_qdisc_cake(self):
5466 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
ef3c8a92 5467 start_networkd()
95e1fbba 5468 self.wait_online('dummy98:routable')
ef3c8a92 5469
4c7d13f4 5470 output = check_output('tc qdisc show dev dummy98')
f1de1eb3 5471 print(output)
4c7d13f4
YW
5472 self.assertIn('qdisc cake 3a: root', output)
5473 self.assertIn('bandwidth 500Mbit', output)
5474 self.assertIn('autorate-ingress', output)
5475 self.assertIn('diffserv8', output)
5476 self.assertIn('dual-dsthost', output)
5477 self.assertIn(' nat', output)
5478 self.assertIn(' wash', output)
5479 self.assertIn(' split-gso', output)
5480 self.assertIn(' raw', output)
5481 self.assertIn(' atm', output)
5482 self.assertIn('overhead 128', output)
5483 self.assertIn('mpu 20', output)
5484 self.assertIn('fwmark 0xff00', output)
77d5f36d
YW
5485 self.assertIn('rtt 1s', output)
5486 self.assertIn('ack-filter-aggressive', output)
4c7d13f4 5487
21d9eeb5
DDM
5488 # Test for replacing existing qdisc. See #31226.
5489 with open(os.path.join(network_unit_dir, '25-qdisc-cake.network'), mode='a', encoding='utf-8') as f:
5490 f.write('Bandwidth=250M\n')
5491
5492 networkctl_reload()
5493 self.wait_online('dummy98:routable')
5494
5495 output = check_output('tc qdisc show dev dummy98')
5496 print(output)
5497 self.assertIn('bandwidth 250Mbit', output)
5498
4c7d13f4
YW
5499 @expectedFailureIfModuleIsNotAvailable('sch_codel')
5500 def test_qdisc_codel(self):
5501 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
5502 start_networkd()
95e1fbba 5503 self.wait_online('dummy98:routable')
f1de1eb3 5504
ef3c8a92
YW
5505 output = check_output('tc qdisc show dev dummy98')
5506 print(output)
4c7d13f4
YW
5507 self.assertRegex(output, 'qdisc codel 33: root')
5508 self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
f1de1eb3 5509
4c7d13f4
YW
5510 @expectedFailureIfModuleIsNotAvailable('sch_drr')
5511 def test_qdisc_drr(self):
5512 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
5513 start_networkd()
95e1fbba 5514 self.wait_online('dummy98:routable')
f1de1eb3 5515
4c7d13f4
YW
5516 output = check_output('tc qdisc show dev dummy98')
5517 print(output)
5518 self.assertRegex(output, 'qdisc drr 2: root')
5519 output = check_output('tc class show dev dummy98')
5520 print(output)
5521 self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
ef3c8a92 5522
4c7d13f4
YW
5523 @expectedFailureIfModuleIsNotAvailable('sch_ets')
5524 def test_qdisc_ets(self):
5525 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
5526 start_networkd()
95e1fbba 5527 self.wait_online('dummy98:routable')
4c7d13f4
YW
5528
5529 output = check_output('tc qdisc show dev dummy98')
5530 print(output)
5531
5532 self.assertRegex(output, 'qdisc ets 3a: root')
5533 self.assertRegex(output, 'bands 10 strict 3')
5534 self.assertRegex(output, 'quanta 1 2 3 4 5')
5535 self.assertRegex(output, 'priomap 3 4 5 6 7')
5536
5537 @expectedFailureIfModuleIsNotAvailable('sch_fq')
5538 def test_qdisc_fq(self):
5539 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
5540 start_networkd()
95e1fbba 5541 self.wait_online('dummy98:routable')
0baddbd5 5542
4c7d13f4
YW
5543 output = check_output('tc qdisc show dev dummy98')
5544 print(output)
5545 self.assertRegex(output, 'qdisc fq 32: root')
a05a6e8b
YW
5546 self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
5547 self.assertRegex(output, 'quantum 1500')
5548 self.assertRegex(output, 'initial_quantum 13000')
5549 self.assertRegex(output, 'maxrate 1Mbit')
ab9dc1db 5550
4c7d13f4
YW
5551 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
5552 def test_qdisc_fq_codel(self):
5553 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
5554 start_networkd()
95e1fbba 5555 self.wait_online('dummy98:routable')
ab9dc1db 5556
4c7d13f4
YW
5557 output = check_output('tc qdisc show dev dummy98')
5558 print(output)
5559 self.assertRegex(output, 'qdisc fq_codel 34: root')
7887e580 5560 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 5561
4c7d13f4
YW
5562 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
5563 def test_qdisc_fq_pie(self):
5564 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
5565 start_networkd()
95e1fbba 5566 self.wait_online('dummy98:routable')
ab9dc1db 5567
4c7d13f4
YW
5568 output = check_output('tc qdisc show dev dummy98')
5569 print(output)
3d55b5a9 5570
4c7d13f4
YW
5571 self.assertRegex(output, 'qdisc fq_pie 3a: root')
5572 self.assertRegex(output, 'limit 200000p')
bc0769c9 5573
4c7d13f4
YW
5574 @expectedFailureIfModuleIsNotAvailable('sch_gred')
5575 def test_qdisc_gred(self):
5576 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
5577 start_networkd()
95e1fbba 5578 self.wait_online('dummy98:routable')
4c7d13f4
YW
5579
5580 output = check_output('tc qdisc show dev dummy98')
5581 print(output)
5582 self.assertRegex(output, 'qdisc gred 38: root')
95edcf3f
YW
5583 self.assertRegex(output, 'vqs 12 default 10 grio')
5584
4c7d13f4
YW
5585 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
5586 def test_qdisc_hhf(self):
5587 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
5588 start_networkd()
95e1fbba 5589 self.wait_online('dummy98:routable')
4c7d13f4
YW
5590
5591 output = check_output('tc qdisc show dev dummy98')
5592 print(output)
5593 self.assertRegex(output, 'qdisc hhf 3a: root')
5594 self.assertRegex(output, 'limit 1022p')
5595
5596 @expectedFailureIfModuleIsNotAvailable('sch_htb')
5597 def test_qdisc_htb_fifo(self):
5598 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
5599 start_networkd()
95e1fbba 5600 self.wait_online('dummy98:routable')
4c7d13f4
YW
5601
5602 output = check_output('tc qdisc show dev dummy98')
5603 print(output)
5604 self.assertRegex(output, 'qdisc htb 2: root')
5605 self.assertRegex(output, r'default (0x30|30)')
5606
5607 self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
5608 self.assertRegex(output, 'limit 100000p')
f2c5c129 5609
7b1a31a3
YW
5610 self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
5611 self.assertRegex(output, 'limit 1000000')
5612
73136507
YW
5613 self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
5614 self.assertRegex(output, 'limit 1023p')
5615
41bb371b
YW
5616 self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
5617
2ee7e54b 5618 output = check_output('tc -d class show dev dummy98')
ab9dc1db 5619 print(output)
8e2449a5
YW
5620 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
5621 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
5622 # which is fixed in v6.3.0 by
5623 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
5624 self.assertRegex(output, 'class htb 2:37 root leaf 37(:|prio) ')
5625 self.assertRegex(output, 'class htb 2:3a root leaf 3a(:|prio) ')
5626 self.assertRegex(output, 'class htb 2:3b root leaf 3b(:|prio) ')
5627 self.assertRegex(output, 'class htb 2:3c root leaf 3c(:|prio) ')
2ee7e54b
YW
5628 self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
5629 self.assertRegex(output, 'burst 123456')
5630 self.assertRegex(output, 'cburst 123457')
0baddbd5 5631
4c7d13f4
YW
5632 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
5633 def test_qdisc_ingress(self):
5634 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
5635 '25-qdisc-ingress.network', '11-dummy.netdev')
557fa421 5636 start_networkd()
95e1fbba 5637 self.wait_online('dummy98:routable', 'test1:routable')
557fa421
YW
5638
5639 output = check_output('tc qdisc show dev dummy98')
5640 print(output)
4c7d13f4 5641 self.assertRegex(output, 'qdisc clsact')
557fa421 5642
891ff963
YW
5643 output = check_output('tc qdisc show dev test1')
5644 print(output)
4c7d13f4 5645 self.assertRegex(output, 'qdisc ingress')
891ff963 5646
2b9ced90
DDM
5647 def test_qdisc_mq(self):
5648 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-mq.network')
5649 start_networkd()
5650 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5651
5652 output = check_output('tc qdisc show dev testtun99')
5653 print(output)
5654 self.assertIn('qdisc mq 2: root', output)
5655
3f14557c
DDM
5656 @expectedFailureIfModuleIsNotAvailable('sch_multiq')
5657 def test_qdisc_multiq(self):
5658 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-multiq.network')
5659 start_networkd()
5660 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5661
5662 output = check_output('tc qdisc show dev testtun99')
5663 print(output)
5664 self.assertIn('qdisc multiq 2: root', output)
5665
4c7d13f4
YW
5666 @expectedFailureIfModuleIsNotAvailable('sch_netem')
5667 def test_qdisc_netem(self):
5668 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
5669 '25-qdisc-netem-compat.network', '11-dummy.netdev')
3d55b5a9 5670 start_networkd()
95e1fbba 5671 self.wait_online('dummy98:routable', 'test1:routable')
3d55b5a9
YW
5672
5673 output = check_output('tc qdisc show dev dummy98')
5674 print(output)
4c7d13f4
YW
5675 self.assertRegex(output, 'qdisc netem 30: root')
5676 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
5677
5678 output = check_output('tc qdisc show dev test1')
5679 print(output)
5680 self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
5681 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
3d55b5a9 5682
854f9899 5683 @expectedFailureIfModuleIsNotAvailable('sch_pie')
be94e591 5684 def test_qdisc_pie(self):
a962d857 5685 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
be94e591 5686 start_networkd()
95e1fbba 5687 self.wait_online('dummy98:routable')
be94e591
YW
5688
5689 output = check_output('tc qdisc show dev dummy98')
5690 print(output)
5691 self.assertRegex(output, 'qdisc pie 3a: root')
5692 self.assertRegex(output, 'limit 200000')
5693
4c7d13f4
YW
5694 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
5695 def test_qdisc_qfq(self):
5696 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
970ab1fc 5697 start_networkd()
95e1fbba 5698 self.wait_online('dummy98:routable')
970ab1fc
YW
5699
5700 output = check_output('tc qdisc show dev dummy98')
5701 print(output)
4c7d13f4
YW
5702 self.assertRegex(output, 'qdisc qfq 2: root')
5703 output = check_output('tc class show dev dummy98')
5704 print(output)
5705 self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
5706 self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
970ab1fc 5707
4c7d13f4
YW
5708 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
5709 def test_qdisc_sfb(self):
5710 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
b753e835 5711 start_networkd()
95e1fbba 5712 self.wait_online('dummy98:routable')
b753e835
YW
5713
5714 output = check_output('tc qdisc show dev dummy98')
5715 print(output)
4c7d13f4
YW
5716 self.assertRegex(output, 'qdisc sfb 39: root')
5717 self.assertRegex(output, 'limit 200000')
1578266b 5718
4c7d13f4
YW
5719 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
5720 def test_qdisc_sfq(self):
5721 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
5722 start_networkd()
95e1fbba 5723 self.wait_online('dummy98:routable')
b753e835 5724
4c7d13f4
YW
5725 output = check_output('tc qdisc show dev dummy98')
5726 print(output)
5727 self.assertRegex(output, 'qdisc sfq 36: root')
5728 self.assertRegex(output, 'perturb 5sec')
5729
5730 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
5731 def test_qdisc_tbf(self):
5732 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
1578266b 5733 start_networkd()
95e1fbba 5734 self.wait_online('dummy98:routable')
1578266b
YW
5735
5736 output = check_output('tc qdisc show dev dummy98')
5737 print(output)
4c7d13f4 5738 self.assertRegex(output, 'qdisc tbf 35: root')
f9a85b74 5739 self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms')
1578266b 5740
4c7d13f4
YW
5741 @expectedFailureIfModuleIsNotAvailable('sch_teql')
5742 def test_qdisc_teql(self):
5743 call_quiet('rmmod sch_teql')
5744
5745 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
5746 start_networkd()
5747 self.wait_links('dummy98')
5748 check_output('modprobe sch_teql max_equalizers=2')
95e1fbba 5749 self.wait_online('dummy98:routable')
4c7d13f4
YW
5750
5751 output = check_output('tc qdisc show dev dummy98')
5752 print(output)
5753 self.assertRegex(output, 'qdisc teql1 31: root')
1578266b 5754
e6fa9119
YW
5755 @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
5756 def test_qdisc_drop(self):
5757 copy_network_unit('12-dummy.netdev', '12-dummy.network')
5758 start_networkd()
5759 self.wait_online('dummy98:routable')
5760
5761 # Test case for issue #32247 and #32254.
5762 for _ in range(20):
5763 check_output('tc qdisc replace dev dummy98 root fq')
5764 self.assertFalse(networkd_is_failed())
5765 check_output('tc qdisc replace dev dummy98 root fq pacing')
5766 self.assertFalse(networkd_is_failed())
5767 check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
5768 self.assertFalse(networkd_is_failed())
5769 check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
5770 self.assertFalse(networkd_is_failed())
5771
336d18f0 5772class NetworkdStateFileTests(unittest.TestCase, Utilities):
336d18f0
YW
5773
5774 def setUp(self):
a962d857 5775 setup_common()
336d18f0
YW
5776
5777 def tearDown(self):
a962d857 5778 tear_down_common()
336d18f0
YW
5779
5780 def test_state_file(self):
a962d857 5781 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
336d18f0 5782 start_networkd()
95e1fbba 5783 self.wait_online('dummy98:routable')
336d18f0 5784
f91b2340 5785 # make link state file updated
10d670a3 5786 resolvectl('revert', 'dummy98')
336d18f0 5787
10d670a3 5788 check_json(networkctl_json())
94f0bd62 5789
1a8e1d78
YW
5790 output = read_link_state_file('dummy98')
5791 print(output)
5792 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5793 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5794 self.assertIn('ADMIN_STATE=configured', output)
5795 self.assertIn('OPER_STATE=routable', output)
5796 self.assertIn('REQUIRED_FOR_ONLINE=yes', output)
5797 self.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output)
5798 self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output)
5799 self.assertIn('ACTIVATION_POLICY=up', output)
5800 self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output)
5801 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5802 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5803 self.assertIn('DOMAINS=hogehoge', output)
5804 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5805 self.assertIn('LLMNR=no', output)
5806 self.assertIn('MDNS=yes', output)
5807 self.assertIn('DNSSEC=no', output)
336d18f0 5808
10d670a3
YW
5809 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
5810 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
5811 resolvectl('llmnr', 'dummy98', 'yes')
5812 resolvectl('mdns', 'dummy98', 'no')
5813 resolvectl('dnssec', 'dummy98', 'yes')
5814 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
336d18f0 5815
10d670a3 5816 check_json(networkctl_json())
94f0bd62 5817
1a8e1d78
YW
5818 output = read_link_state_file('dummy98')
5819 print(output)
5820 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5821 self.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output)
5822 self.assertIn('DOMAINS=hogehogehoge', output)
5823 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5824 self.assertIn('LLMNR=yes', output)
5825 self.assertIn('MDNS=no', output)
5826 self.assertIn('DNSSEC=yes', output)
336d18f0 5827
10d670a3 5828 timedatectl('revert', 'dummy98')
336d18f0 5829
10d670a3 5830 check_json(networkctl_json())
94f0bd62 5831
1a8e1d78
YW
5832 output = read_link_state_file('dummy98')
5833 print(output)
5834 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5835 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5836 self.assertIn('DOMAINS=hogehogehoge', output)
5837 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5838 self.assertIn('LLMNR=yes', output)
5839 self.assertIn('MDNS=no', output)
5840 self.assertIn('DNSSEC=yes', output)
336d18f0 5841
10d670a3 5842 resolvectl('revert', 'dummy98')
336d18f0 5843
10d670a3 5844 check_json(networkctl_json())
94f0bd62 5845
1a8e1d78
YW
5846 output = read_link_state_file('dummy98')
5847 print(output)
5848 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5849 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5850 self.assertIn('DOMAINS=hogehoge', output)
5851 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5852 self.assertIn('LLMNR=no', output)
5853 self.assertIn('MDNS=yes', output)
5854 self.assertIn('DNSSEC=no', output)
336d18f0 5855
ee3cbfdb
YW
5856 def test_address_state(self):
5857 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
5858 start_networkd()
5859
95e1fbba 5860 self.wait_online('dummy98:degraded')
ee3cbfdb
YW
5861
5862 output = read_link_state_file('dummy98')
5863 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5864 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5865
5866 # with a routable IPv4 address
5867 check_output('ip address add 10.1.2.3/16 dev dummy98')
95e1fbba
YW
5868 self.wait_online('dummy98:routable', ipv4=True)
5869 self.wait_online('dummy98:routable')
ee3cbfdb
YW
5870
5871 output = read_link_state_file('dummy98')
5872 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5873 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5874
5875 check_output('ip address del 10.1.2.3/16 dev dummy98')
5876
5877 # with a routable IPv6 address
5878 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
95e1fbba
YW
5879 self.wait_online('dummy98:routable', ipv6=True)
5880 self.wait_online('dummy98:routable')
ee3cbfdb
YW
5881
5882 output = read_link_state_file('dummy98')
5883 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5884 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5885
be68c2c9 5886class NetworkdBondTests(unittest.TestCase, Utilities):
c3a8853f
YW
5887
5888 def setUp(self):
a962d857 5889 setup_common()
c3a8853f
YW
5890
5891 def tearDown(self):
a962d857 5892 tear_down_common()
c3a8853f 5893
b06469a6
YW
5894 def test_bond_keep_master(self):
5895 check_output('ip link add bond199 type bond mode active-backup')
5896 check_output('ip link add dummy98 type dummy')
5897 check_output('ip link set dummy98 master bond199')
5898
a962d857 5899 copy_network_unit('23-keep-master.network')
b06469a6 5900 start_networkd()
95e1fbba 5901 self.wait_online('dummy98:enslaved')
b06469a6
YW
5902
5903 output = check_output('ip -d link show bond199')
5904 print(output)
5905 self.assertRegex(output, 'active_slave dummy98')
5906
5907 output = check_output('ip -d link show dummy98')
5908 print(output)
5909 self.assertRegex(output, 'master bond199')
5910
b1bed07d
YW
5911 # Test case for #37629
5912 for _ in range(3):
5913 # When a slave leaved from its master bonding interface, the kernel brings down the slave.
5914 check_output('ip link set dummy98 nomaster')
5915 self.wait_online('dummy98:off')
5916
5917 # Bring up the interface to check if networkd recognizes the interface has no master now.
5918 check_output('ip link set dummy98 up')
5919 self.wait_online('dummy98:carrier')
5920
5921 # We need to first bring down the interface to make it join a bonding interface.
5922 check_output('ip link set dummy98 down')
5923 check_output('ip link set dummy98 master bond199')
5924 self.wait_online('dummy98:enslaved')
5925
c2990ec3 5926 def test_bond_active_slave(self):
a962d857 5927 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 5928 start_networkd()
95e1fbba 5929 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 5930
371810d1 5931 output = check_output('ip -d link show bond199')
c2990ec3 5932 print(output)
b448fc0a 5933 self.assertIn('active_slave dummy98', output)
c2990ec3 5934
2bb1d3c1
YW
5935 # test case for issue #31165.
5936 since = datetime.datetime.now()
5937 networkctl_reconfigure('dummy98')
95e1fbba 5938 self.wait_online('dummy98:enslaved', 'bond199:degraded')
2bb1d3c1
YW
5939 self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
5940
5b73edfa
YW
5941 # test for reloading.
5942 touch_network_unit(
5943 '23-active-slave.network',
5944 '23-bond199.network',
5945 '25-bond-active-backup-slave.netdev',
5946 '12-dummy.netdev')
5947 networkctl_reload()
5948 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5949
c2990ec3 5950 def test_bond_primary_slave(self):
a962d857 5951 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2cf6fdff 5952 start_networkd()
95e1fbba 5953 self.wait_online('dummy98:enslaved', 'bond199:degraded')
c2990ec3 5954
371810d1 5955 output = check_output('ip -d link show bond199')
c2990ec3 5956 print(output)
b448fc0a
YW
5957 self.assertIn('primary dummy98', output)
5958
5959 # for issue #25627
5960 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
5961 for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
5962 with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
5963 f.write(f'[Link]\nMACAddress={mac}\n')
5964
5965 networkctl_reload()
95e1fbba 5966 self.wait_online('dummy98:enslaved', 'bond199:degraded')
b448fc0a
YW
5967
5968 output = check_output('ip -d link show bond199')
5969 print(output)
5970 self.assertIn(f'link/ether {mac}', output)
c2990ec3 5971
cc3e488c 5972 def test_bond_operstate(self):
a962d857
YW
5973 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
5974 '25-bond99.network', '25-bond-slave.network')
2cf6fdff 5975 start_networkd()
95e1fbba 5976 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
c3a8853f 5977
371810d1 5978 output = check_output('ip -d link show dummy98')
c3a8853f 5979 print(output)
cc3e488c 5980 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
c3a8853f 5981
371810d1 5982 output = check_output('ip -d link show test1')
c3a8853f
YW
5983 print(output)
5984 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
5985
371810d1 5986 output = check_output('ip -d link show bond99')
c3a8853f
YW
5987 print(output)
5988 self.assertRegex(output, 'MASTER,UP,LOWER_UP')
5989
c6aae2cd
YW
5990 # test case for issue #32186
5991 restart_networkd()
5992 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
5993
19cf3143
DS
5994 self.wait_operstate('dummy98', 'enslaved')
5995 self.wait_operstate('test1', 'enslaved')
5996 self.wait_operstate('bond99', 'routable')
c3a8853f 5997
cefd6b3d 5998 check_output('ip link set dummy98 down')
c3a8853f 5999
19cf3143
DS
6000 self.wait_operstate('dummy98', 'off')
6001 self.wait_operstate('test1', 'enslaved')
cf4dbd84 6002 self.wait_operstate('bond99', 'routable')
c3a8853f 6003
cefd6b3d 6004 check_output('ip link set dummy98 up')
c3a8853f 6005
19cf3143
DS
6006 self.wait_operstate('dummy98', 'enslaved')
6007 self.wait_operstate('test1', 'enslaved')
6008 self.wait_operstate('bond99', 'routable')
c3a8853f 6009
cefd6b3d
ZJS
6010 check_output('ip link set dummy98 down')
6011 check_output('ip link set test1 down')
cc3e488c 6012
19cf3143
DS
6013 self.wait_operstate('dummy98', 'off')
6014 self.wait_operstate('test1', 'off')
2700d2c7 6015
a4632dc7 6016 if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
2700d2c7
YW
6017 # Huh? Kernel does not recognize that all slave interfaces are down?
6018 # Let's confirm that networkd's operstate is consistent with ip's result.
88825195
YW
6019 output = check_output('ip -d link show bond99')
6020 print(output)
2700d2c7 6021 self.assertNotRegex(output, 'NO-CARRIER')
cc3e488c 6022
be68c2c9 6023class NetworkdBridgeTests(unittest.TestCase, Utilities):
8d17c386 6024
1f0e3109 6025 def setUp(self):
a962d857 6026 setup_common()
1f0e3109
SS
6027
6028 def tearDown(self):
a962d857 6029 tear_down_common()
1f0e3109 6030
9540f8e2
YW
6031 def test_bridge_mac_none(self):
6032 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
6033 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
6034 start_networkd()
95e1fbba 6035 self.wait_online('dummy98:enslaved', 'bridge99:degraded')
9540f8e2
YW
6036
6037 output = check_output('ip link show dev dummy98')
6038 print(output)
6039 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6040
6041 output = check_output('ip link show dev bridge99')
6042 print(output)
6043 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6044
6f943798 6045 def test_bridge_vlan(self):
a962d857 6046 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
60f4b2c5
YW
6047 '26-bridge.netdev', '26-bridge-vlan-master.network',
6048 copy_dropins=False)
6f943798 6049 start_networkd()
95e1fbba 6050 self.wait_online('test1:enslaved', 'bridge99:degraded')
6f943798
YW
6051
6052 output = check_output('bridge vlan show dev test1')
6053 print(output)
60f4b2c5
YW
6054 # check if the default VID is removed
6055 self.assertNotIn('1 Egress Untagged', output)
6056 for i in range(1000, 3000):
6057 if i == 1010:
6058 self.assertIn(f'{i} PVID', output)
6059 elif i in range(1012, 1016) or i in range(1103, 1109):
6060 self.assertIn(f'{i} Egress Untagged', output)
6061 elif i in range(1008, 1014) or i in range(1100, 1111):
6062 self.assertIn(f'{i}', output)
6063 else:
6064 self.assertNotIn(f'{i}', output)
6065
6066 output = check_output('bridge vlan show dev bridge99')
6067 print(output)
6068 # check if the default VID is removed
6069 self.assertNotIn('1 Egress Untagged', output)
6070 for i in range(1000, 3000):
6071 if i == 1020:
6072 self.assertIn(f'{i} PVID', output)
6073 elif i in range(1022, 1026) or i in range(1203, 1209):
6074 self.assertIn(f'{i} Egress Untagged', output)
6075 elif i in range(1018, 1024) or i in range(1200, 1211):
6076 self.assertIn(f'{i}', output)
6077 else:
6078 self.assertNotIn(f'{i}', output)
6079
6080 # Change vlan IDs
6081 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
6082 '26-bridge-vlan-master.network.d/10-override.conf')
6083 networkctl_reload()
95e1fbba 6084 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
6085
6086 output = check_output('bridge vlan show dev test1')
6087 print(output)
6088 for i in range(1000, 3000):
6089 if i == 2010:
6090 self.assertIn(f'{i} PVID', output)
6091 elif i in range(2012, 2016) or i in range(2103, 2109):
6092 self.assertIn(f'{i} Egress Untagged', output)
6093 elif i in range(2008, 2014) or i in range(2100, 2111):
6094 self.assertIn(f'{i}', output)
6095 else:
6096 self.assertNotIn(f'{i}', output)
6097
6098 output = check_output('bridge vlan show dev bridge99')
6099 print(output)
6100 for i in range(1000, 3000):
6101 if i == 2020:
6102 self.assertIn(f'{i} PVID', output)
6103 elif i in range(2022, 2026) or i in range(2203, 2209):
6104 self.assertIn(f'{i} Egress Untagged', output)
6105 elif i in range(2018, 2024) or i in range(2200, 2211):
6106 self.assertIn(f'{i}', output)
6107 else:
6108 self.assertNotIn(f'{i}', output)
6109
6110 # Remove several vlan IDs
6111 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
6112 '26-bridge-vlan-master.network.d/20-override.conf')
6113 networkctl_reload()
95e1fbba 6114 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
6115
6116 output = check_output('bridge vlan show dev test1')
6117 print(output)
6118 for i in range(1000, 3000):
6119 if i == 2010:
6120 self.assertIn(f'{i} PVID', output)
6121 elif i in range(2012, 2016):
6122 self.assertIn(f'{i} Egress Untagged', output)
6123 elif i in range(2008, 2014):
6124 self.assertIn(f'{i}', output)
6125 else:
6126 self.assertNotIn(f'{i}', output)
6127
6128 output = check_output('bridge vlan show dev bridge99')
6129 print(output)
6130 for i in range(1000, 3000):
6131 if i == 2020:
6132 self.assertIn(f'{i} PVID', output)
6133 elif i in range(2022, 2026):
6134 self.assertIn(f'{i} Egress Untagged', output)
6135 elif i in range(2018, 2024):
6136 self.assertIn(f'{i}', output)
6137 else:
6138 self.assertNotIn(f'{i}', output)
6139
6140 # Remove all vlan IDs
6141 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
6142 '26-bridge-vlan-master.network.d/30-override.conf')
6143 networkctl_reload()
95e1fbba 6144 self.wait_online('test1:enslaved', 'bridge99:degraded')
60f4b2c5
YW
6145
6146 output = check_output('bridge vlan show dev test1')
6147 print(output)
6148 self.assertNotIn('PVID', output)
6149 for i in range(1000, 3000):
6150 self.assertNotIn(f'{i}', output)
6f943798
YW
6151
6152 output = check_output('bridge vlan show dev bridge99')
6153 print(output)
60f4b2c5
YW
6154 self.assertNotIn('PVID', output)
6155 for i in range(1000, 3000):
6156 self.assertNotIn(f'{i}', output)
6f943798 6157
988b0660 6158 def test_bridge_vlan_issue_20373(self):
a962d857
YW
6159 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
6160 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
6161 '21-vlan.netdev', '21-vlan.network')
988b0660 6162 start_networkd()
95e1fbba 6163 self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
988b0660
YW
6164
6165 output = check_output('bridge vlan show dev test1')
6166 print(output)
6167 self.assertIn('100 PVID Egress Untagged', output)
6168 self.assertIn('560', output)
6169 self.assertIn('600', output)
6170
6171 output = check_output('bridge vlan show dev bridge99')
6172 print(output)
6173 self.assertIn('1 PVID Egress Untagged', output)
6174 self.assertIn('100', output)
6175 self.assertIn('600', output)
6176
cc0276cc 6177 def test_bridge_mdb(self):
a962d857
YW
6178 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
6179 '26-bridge.netdev', '26-bridge-mdb-master.network')
cc0276cc 6180 start_networkd()
95e1fbba 6181 self.wait_online('test1:enslaved', 'bridge99:degraded')
cc0276cc
YW
6182
6183 output = check_output('bridge mdb show dev bridge99')
6184 print(output)
6185 self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
6186 self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
6187
9f773037 6188 # Old kernel may not support bridge MDB entries on bridge master
a962d857 6189 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
9f773037
YW
6190 self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
6191 self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
6192
82f2a2f0 6193 # Old kernel may not support L2 bridge MDB entries
6194 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0:
6195 self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069')
6196
b06469a6
YW
6197 def test_bridge_keep_master(self):
6198 check_output('ip link add bridge99 type bridge')
6199 check_output('ip link set bridge99 up')
6200 check_output('ip link add dummy98 type dummy')
6201 check_output('ip link set dummy98 master bridge99')
6202
a962d857 6203 copy_network_unit('23-keep-master.network')
b06469a6 6204 start_networkd()
95e1fbba 6205 self.wait_online('dummy98:enslaved')
b06469a6
YW
6206
6207 output = check_output('ip -d link show dummy98')
6208 print(output)
6209 self.assertRegex(output, 'master bridge99')
6210 self.assertRegex(output, 'bridge')
6211
6212 output = check_output('bridge -d link show dummy98')
6213 print(output)
a962d857
YW
6214 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6215 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6216 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6217 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6218 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
b06469a6 6219 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
6220 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6221 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6222 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6223 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
6224 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6225 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
b06469a6 6226
5b73edfa 6227 def check_bridge_property(self):
95e1fbba 6228 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
1f0e3109 6229
21d0ed68
YW
6230 output = check_output('ip -d link show bridge99')
6231 print(output)
6232 self.assertIn('mtu 9000 ', output)
6233
371810d1 6234 output = check_output('ip -d link show test1')
1f0e3109 6235 print(output)
21d0ed68
YW
6236 self.assertIn('master bridge99 ', output)
6237 self.assertIn('bridge_slave', output)
6238 self.assertIn('mtu 9000 ', output)
1f0e3109 6239
371810d1 6240 output = check_output('ip -d link show dummy98')
1f0e3109 6241 print(output)
21d0ed68
YW
6242 self.assertIn('master bridge99 ', output)
6243 self.assertIn('bridge_slave', output)
6244 self.assertIn('mtu 9000 ', output)
1f0e3109 6245
371810d1 6246 output = check_output('ip addr show bridge99')
1f0e3109 6247 print(output)
21d0ed68 6248 self.assertIn('192.168.0.15/24', output)
1f0e3109 6249
371810d1 6250 output = check_output('bridge -d link show dummy98')
1f0e3109 6251 print(output)
a962d857
YW
6252 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6253 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6254 self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
6255 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6256 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6257 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5424fd95 6258 # CONFIG_BRIDGE_IGMP_SNOOPING=y
a962d857
YW
6259 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6260 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6261 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6262 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
3f504b89
YW
6263 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6264 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4d7ed14f 6265
5424fd95
YW
6266 output = check_output('bridge -d link show test1')
6267 print(output)
a962d857 6268 self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
a434de60 6269 self.assertIn('locked on', output)
08a26ecc
JG
6270 if ' mab ' in output: # This is new in kernel and iproute2 v6.2
6271 self.assertIn('mab on', output)
1f0e3109 6272
5b73edfa
YW
6273 def test_bridge_property(self):
6274 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6275 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
c88adbc0 6276 '25-bridge99.network', '14-dummy.netdev', '26-bridge-vlan-tunnel.network')
5b73edfa
YW
6277 start_networkd()
6278 self.check_bridge_property()
6279
6280 # test reload
6281 touch_network_unit(
6282 '11-dummy.netdev',
6283 '12-dummy.netdev',
6284 '26-bridge.netdev',
6285 '26-bridge-slave-interface-1.network',
6286 '26-bridge-slave-interface-2.network',
c88adbc0 6287 '26-bridge-vlan-tunnel.network',
5b73edfa
YW
6288 '25-bridge99.network')
6289 networkctl_reload()
6290 self.check_bridge_property()
6291
371810d1 6292 check_output('ip address add 192.168.0.16/24 dev bridge99')
371810d1 6293 output = check_output('ip addr show bridge99')
2be6c5d2 6294 print(output)
21d0ed68 6295 self.assertIn('192.168.0.16/24', output)
2be6c5d2 6296
e3cbaeab
YW
6297 # for issue #6088
6298 print('### ip -6 route list table all dev bridge99')
6299 output = check_output('ip -6 route list table all dev bridge99')
6300 print(output)
beb75dd3 6301 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 6302
a962d857 6303 remove_link('test1')
cf4dbd84 6304 self.wait_operstate('bridge99', 'routable')
2be6c5d2 6305
21d0ed68
YW
6306 output = check_output('ip -d link show bridge99')
6307 print(output)
6308 self.assertIn('mtu 9000 ', output)
6309
6310 output = check_output('ip -d link show dummy98')
6311 print(output)
6312 self.assertIn('master bridge99 ', output)
6313 self.assertIn('bridge_slave', output)
6314 self.assertIn('mtu 9000 ', output)
804b6cd2 6315
c88adbc0
EL
6316 output = check_output('ip -d link show dummy97')
6317 self.assertIn('vlan_tunnel on ', output)
6318
21d0ed68 6319 remove_link('dummy98')
c88adbc0 6320 remove_link('dummy97')
19cf3143 6321 self.wait_operstate('bridge99', 'no-carrier')
2be6c5d2 6322
21d0ed68
YW
6323 output = check_output('ip -d link show bridge99')
6324 print(output)
6325 # When no carrier, the kernel may reset the MTU
6326 self.assertIn('NO-CARRIER', output)
6327
371810d1 6328 output = check_output('ip address show bridge99')
804b6cd2 6329 print(output)
21d0ed68
YW
6330 self.assertNotIn('192.168.0.15/24', output)
6331 self.assertIn('192.168.0.16/24', output) # foreign address is kept
804b6cd2 6332
e3cbaeab
YW
6333 print('### ip -6 route list table all dev bridge99')
6334 output = check_output('ip -6 route list table all dev bridge99')
6335 print(output)
beb75dd3 6336 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
e3cbaeab 6337
21d0ed68 6338 check_output('ip link add dummy98 type dummy')
95e1fbba 6339 self.wait_online('dummy98:enslaved', 'bridge99:routable')
21d0ed68
YW
6340
6341 output = check_output('ip -d link show bridge99')
6342 print(output)
6343 self.assertIn('mtu 9000 ', output)
6344
6345 output = check_output('ip -d link show dummy98')
6346 print(output)
6347 self.assertIn('master bridge99 ', output)
6348 self.assertIn('bridge_slave', output)
6349 self.assertIn('mtu 9000 ', output)
6350
0fc0d85f 6351 def test_bridge_configure_without_carrier(self):
a962d857
YW
6352 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
6353 '11-dummy.netdev')
0fc0d85f
DS
6354 start_networkd()
6355
6356 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
6357 for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
6358 with self.subTest(test=test):
6359 if test == 'no-slave':
6360 # bridge has no slaves; it's up but *might* not have carrier
001c07cf 6361 self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
0fc0d85f
DS
6362 # due to a bug in the kernel, newly-created bridges are brought up
6363 # *with* carrier, unless they have had any setting changed; e.g.
6364 # their mac set, priority set, etc. Then, they will lose carrier
6365 # as soon as a (down) slave interface is added, and regain carrier
6366 # again once the slave interface is brought up.
6367 #self.check_link_attr('bridge99', 'carrier', '0')
6368 elif test == 'add-slave':
6369 # add slave to bridge, but leave it down; bridge is definitely no-carrier
6370 self.check_link_attr('test1', 'operstate', 'down')
6371 check_output('ip link set dev test1 master bridge99')
001c07cf 6372 self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
0fc0d85f
DS
6373 self.check_link_attr('bridge99', 'carrier', '0')
6374 elif test == 'slave-up':
6375 # bring up slave, which will have carrier; bridge gains carrier
6376 check_output('ip link set dev test1 up')
95e1fbba 6377 self.wait_online('bridge99:routable')
0fc0d85f
DS
6378 self.check_link_attr('bridge99', 'carrier', '1')
6379 elif test == 'slave-no-carrier':
6380 # drop slave carrier; bridge loses carrier
6381 check_output('ip link set dev test1 carrier off')
95e1fbba 6382 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
6383 self.check_link_attr('bridge99', 'carrier', '0')
6384 elif test == 'slave-carrier':
6385 # restore slave carrier; bridge gains carrier
6386 check_output('ip link set dev test1 carrier on')
95e1fbba 6387 self.wait_online('bridge99:routable')
0fc0d85f
DS
6388 self.check_link_attr('bridge99', 'carrier', '1')
6389 elif test == 'slave-down':
6390 # bring down slave; bridge loses carrier
6391 check_output('ip link set dev test1 down')
95e1fbba 6392 self.wait_online('bridge99:no-carrier:no-carrier')
0fc0d85f
DS
6393 self.check_link_attr('bridge99', 'carrier', '0')
6394
10d670a3 6395 output = networkctl_status('bridge99')
0fc0d85f
DS
6396 self.assertRegex(output, '10.1.2.3')
6397 self.assertRegex(output, '10.1.2.1')
6398
804b6cd2 6399 def test_bridge_ignore_carrier_loss(self):
a962d857
YW
6400 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6401 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
6402 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 6403 start_networkd()
95e1fbba 6404 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
804b6cd2 6405
371810d1 6406 check_output('ip address add 192.168.0.16/24 dev bridge99')
a962d857 6407 remove_link('test1', 'dummy98')
804b6cd2
YW
6408 time.sleep(3)
6409
371810d1 6410 output = check_output('ip address show bridge99')
804b6cd2
YW
6411 print(output)
6412 self.assertRegex(output, 'NO-CARRIER')
6413 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6414 self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
6415
6609924c 6416 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
a962d857
YW
6417 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
6418 '25-bridge99-ignore-carrier-loss.network')
2cf6fdff 6419 start_networkd()
95e1fbba 6420 self.wait_online('bridge99:no-carrier')
6609924c 6421
90e3bcbd
YW
6422 for trial in range(4):
6423 check_output('ip link add dummy98 type dummy')
6424 check_output('ip link set dummy98 up')
6425 if trial < 3:
a962d857 6426 remove_link('dummy98')
6609924c 6427
95e1fbba 6428 self.wait_online('bridge99:routable', 'dummy98:enslaved')
6609924c 6429
371810d1 6430 output = check_output('ip address show bridge99')
6609924c
YW
6431 print(output)
6432 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6433
371810d1 6434 output = check_output('ip rule list table 100')
6609924c 6435 print(output)
c4f7a347 6436 self.assertIn('from all to 8.8.8.8 lookup 100', output)
6609924c 6437
a03ff4c0 6438class NetworkdSRIOVTests(unittest.TestCase, Utilities):
a03ff4c0
YW
6439
6440 def setUp(self):
a962d857 6441 setup_common()
a03ff4c0
YW
6442
6443 def tearDown(self):
a962d857 6444 tear_down_common()
a03ff4c0 6445
12e0d6ed 6446 def setup_netdevsim(self, id=99, num_ports=1, num_vfs=0):
a962d857 6447 call('modprobe netdevsim')
a03ff4c0 6448
12e0d6ed 6449 # Create netdevsim device.
d45476ef 6450 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
12e0d6ed
YW
6451 f.write(f'{id} {num_ports}')
6452
6453 # Create VF.
6454 if num_vfs > 0:
6455 with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f:
6456 f.write(f'{num_vfs}')
6457
6458 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6459 def test_sriov(self):
f1f1be71 6460 copy_network_unit('25-netdevsim.link', '25-sriov.network')
a03ff4c0 6461
12e0d6ed 6462 self.setup_netdevsim(num_vfs=3)
a03ff4c0 6463
a03ff4c0 6464 start_networkd()
f1f1be71 6465 self.wait_online('sim99:routable')
a03ff4c0 6466
f1f1be71 6467 output = check_output('ip link show dev sim99')
a03ff4c0
YW
6468 print(output)
6469 self.assertRegex(output,
6470 '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 *'
6471 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6472 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 6473 )
a03ff4c0 6474
1e8e9730
YW
6475 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6476 def test_sriov_udev(self):
a962d857 6477 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
1e8e9730 6478
12e0d6ed 6479 self.setup_netdevsim()
1e8e9730
YW
6480
6481 start_networkd()
f1f1be71 6482 self.wait_online('sim99:routable')
1e8e9730 6483
f1f1be71
YW
6484 # The name sim99 is an alternative name, and cannot be used by udevadm below.
6485 ifname = link_resolve('sim99')
b95d35b5 6486
f1f1be71 6487 output = check_output('ip link show dev sim99')
1e8e9730
YW
6488 print(output)
6489 self.assertRegex(output,
6490 '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 *'
6491 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6492 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 6493 )
1e8e9730
YW
6494 self.assertNotIn('vf 3', output)
6495 self.assertNotIn('vf 4', output)
6496
a962d857 6497 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
6498 f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
6499
a39a2a81
YW
6500 udevadm_reload()
6501 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730 6502
f1f1be71 6503 output = check_output('ip link show dev sim99')
1e8e9730
YW
6504 print(output)
6505 self.assertRegex(output,
6506 '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 *'
6507 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6508 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6509 'vf 3'
a962d857 6510 )
1e8e9730
YW
6511 self.assertNotIn('vf 4', output)
6512
a962d857 6513 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
6514 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6515
a39a2a81
YW
6516 udevadm_reload()
6517 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730 6518
f1f1be71 6519 output = check_output('ip link show dev sim99')
1e8e9730
YW
6520 print(output)
6521 self.assertRegex(output,
6522 '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 *'
6523 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6524 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6525 'vf 3'
a962d857 6526 )
1e8e9730
YW
6527 self.assertNotIn('vf 4', output)
6528
a962d857 6529 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
6530 f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
6531
a39a2a81
YW
6532 udevadm_reload()
6533 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730 6534
f1f1be71 6535 output = check_output('ip link show dev sim99')
1e8e9730
YW
6536 print(output)
6537 self.assertRegex(output,
6538 '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 *'
6539 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
a962d857 6540 )
1e8e9730
YW
6541 self.assertNotIn('vf 2', output)
6542 self.assertNotIn('vf 3', output)
6543 self.assertNotIn('vf 4', output)
6544
a962d857 6545 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
1e8e9730
YW
6546 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6547
a39a2a81
YW
6548 udevadm_reload()
6549 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
1e8e9730 6550
f1f1be71 6551 output = check_output('ip link show dev sim99')
1e8e9730
YW
6552 print(output)
6553 self.assertRegex(output,
6554 '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 *'
6555 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6556 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
a962d857 6557 )
1e8e9730
YW
6558 self.assertNotIn('vf 3', output)
6559 self.assertNotIn('vf 4', output)
6560
be68c2c9 6561class NetworkdLLDPTests(unittest.TestCase, Utilities):
1f0e3109
SS
6562
6563 def setUp(self):
a962d857 6564 setup_common()
1f0e3109
SS
6565
6566 def tearDown(self):
a962d857 6567 tear_down_common()
1f0e3109
SS
6568
6569 def test_lldp(self):
a962d857 6570 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
2cf6fdff 6571 start_networkd()
95e1fbba 6572 self.wait_online('veth99:degraded', 'veth-peer:degraded')
1f0e3109 6573
7ef26afc 6574 for _ in range(20):
10d670a3 6575 output = networkctl('lldp')
f0d87798 6576 print(output)
0f805b46 6577 if re.search(r'veth99 .* veth-peer .* .......a...', output):
f0d87798 6578 break
7ef26afc 6579 time.sleep(0.5)
f0d87798
YW
6580 else:
6581 self.fail()
1f0e3109 6582
d6360819
YW
6583 # With interface name
6584 output = networkctl('lldp', 'veth99');
6585 print(output)
0f805b46 6586 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
6587
6588 # With interface name pattern
6589 output = networkctl('lldp', 've*9');
6590 print(output)
0f805b46 6591 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
d6360819
YW
6592
6593 # json format
6594 output = networkctl('--json=short', 'lldp')
6595 print(output)
6596 self.assertIn('"InterfaceName":"veth99"', output)
6597 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 6598 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
6599
6600 # json format with interface name
6601 output = networkctl('--json=short', 'lldp', 'veth99')
6602 print(output)
6603 self.assertIn('"InterfaceName":"veth99"', output)
6604 self.assertIn('"PortID":"veth-peer"', output)
0f805b46 6605 self.assertIn('"EnabledCapabilities":128', output)
d6360819
YW
6606
6607 # json format with interface name pattern
6608 output = networkctl('--json=short', 'lldp', 've*9')
6609 print(output)
6610 self.assertIn('"InterfaceName":"veth99"', output)
6611 self.assertIn('"PortID":"veth-peer"', output)
0f805b46
YW
6612 self.assertIn('"EnabledCapabilities":128', output)
6613
6614 # LLDP neighbors in status
6615 output = networkctl_status('veth99')
6616 print(output)
6617 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6618
6619 # enable forwarding, to enable the Router flag
6620 with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
6621 f.write('[Network]\nIPv4Forwarding=yes\n')
6622
6623 networkctl_reload()
6624 self.wait_online('veth-peer:degraded')
6625
7ef26afc 6626 for _ in range(20):
0f805b46
YW
6627 output = networkctl('lldp')
6628 print(output)
6629 if re.search(r'veth99 .* veth-peer .* ....r......', output):
6630 break
7ef26afc 6631 time.sleep(0.5)
0f805b46
YW
6632 else:
6633 self.fail()
6634
6635 # With interface name
6636 output = networkctl('lldp', 'veth99');
6637 print(output)
6638 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6639
6640 # With interface name pattern
6641 output = networkctl('lldp', 've*9');
6642 print(output)
6643 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6644
6645 # json format
6646 output = networkctl('--json=short', 'lldp')
6647 print(output)
6648 self.assertIn('"InterfaceName":"veth99"', output)
6649 self.assertIn('"PortID":"veth-peer"', output)
6650 self.assertIn('"EnabledCapabilities":16', output)
6651
6652 # json format with interface name
6653 output = networkctl('--json=short', 'lldp', 'veth99')
6654 print(output)
6655 self.assertIn('"InterfaceName":"veth99"', output)
6656 self.assertIn('"PortID":"veth-peer"', output)
6657 self.assertIn('"EnabledCapabilities":16', output)
6658
6659 # json format with interface name pattern
6660 output = networkctl('--json=short', 'lldp', 've*9')
6661 print(output)
6662 self.assertIn('"InterfaceName":"veth99"', output)
6663 self.assertIn('"PortID":"veth-peer"', output)
6664 self.assertIn('"EnabledCapabilities":16', output)
d6360819
YW
6665
6666 # LLDP neighbors in status
6667 output = networkctl_status('veth99')
6668 print(output)
6669 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6670
e08fdfdd
YW
6671 # Compare the json output from sender and receiver
6672 sender_json = get_link_description('veth-peer')['LLDP']
6673 receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0]
6674 print(sender_json)
6675 print(receiver_json)
6676 self.assertEqual(sender_json, receiver_json)
6677
be68c2c9 6678class NetworkdRATests(unittest.TestCase, Utilities):
1f0e3109
SS
6679
6680 def setUp(self):
a962d857 6681 setup_common()
1f0e3109
SS
6682
6683 def tearDown(self):
a962d857 6684 tear_down_common()
1f0e3109
SS
6685
6686 def test_ipv6_prefix_delegation(self):
a962d857 6687 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
c742d7e8
TM
6688 self.setup_nftset('addr6', 'ipv6_addr')
6689 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6690 self.setup_nftset('ifindex', 'iface_index')
2cf6fdff 6691 start_networkd()
95e1fbba 6692 self.wait_online('veth99:routable', 'veth-peer:degraded')
1f0e3109 6693
3976c430
YW
6694 # IPv6SendRA=yes implies IPv6Forwarding.
6695 self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
6696
10d670a3 6697 output = resolvectl('dns', 'veth99')
41fd8fe7
YW
6698 print(output)
6699 self.assertRegex(output, 'fe80::')
6700 self.assertRegex(output, '2002:da8:1::1')
6701
10d670a3 6702 output = resolvectl('domain', 'veth99')
80762ccc
YW
6703 print(output)
6704 self.assertIn('hogehoge.test', output)
6705
10d670a3 6706 output = networkctl_status('veth99')
1f0e3109
SS
6707 print(output)
6708 self.assertRegex(output, '2002:da8:1:0')
6709
0fe4a1c8
YW
6710 self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
6711 self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
6712
a4640bed
TM
6713 self.check_netlabel('veth99', '2002:da8:1::/64')
6714 self.check_netlabel('veth99', '2002:da8:2::/64')
6715
c742d7e8
TM
6716 self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6717 self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6718 self.check_nftset('network6', '2002:da8:1::/64')
6719 self.check_nftset('network6', '2002:da8:2::/64')
6720 self.check_nftset('ifindex', 'veth99')
6721
6722 self.teardown_nftset('addr6', 'network6', 'ifindex')
6723
6d1cea7b 6724 def check_ipv6_token_static(self):
95e1fbba 6725 self.wait_online('veth99:routable', 'veth-peer:degraded')
b241fa00 6726
10d670a3 6727 output = networkctl_status('veth99')
b241fa00
KF
6728 print(output)
6729 self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
68248f43
YW
6730 self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
6731 self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
6732 self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
b241fa00 6733
6d1cea7b
YW
6734 def test_ipv6_token_static(self):
6735 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6736 start_networkd()
6737
6738 self.check_ipv6_token_static()
6739
6740 for _ in range(20):
6741 check_output('ip link set veth99 down')
6742 check_output('ip link set veth99 up')
6743
6744 self.check_ipv6_token_static()
6745
6746 for _ in range(20):
6747 check_output('ip link set veth99 down')
6748 time.sleep(random.uniform(0, 0.1))
6749 check_output('ip link set veth99 up')
6750 time.sleep(random.uniform(0, 0.1))
6751
6752 self.check_ipv6_token_static()
6753
9dcdf16b
YW
6754 def test_ndisc_redirect(self):
6755 if not os.path.exists(test_ndisc_send):
6756 self.skipTest(f"{test_ndisc_send} does not exist.")
6757
6758 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6759 start_networkd()
6760
6761 self.check_ipv6_token_static()
6762
01420b2d 6763 # Introduce three redirect routes.
9dcdf16b 6764 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
9944629e 6765 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
01420b2d 6766 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d')
9944629e
YW
6767 self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6768 self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
01420b2d 6769 self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
9dcdf16b
YW
6770
6771 # Change the target address of the redirects.
9944629e
YW
6772 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
6773 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
6774 self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6775 self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
0f8afaf9
YW
6776 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d nhid [0-9]* via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
6777 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d nhid [0-9]* via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
9dcdf16b
YW
6778
6779 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
6780 # Then, verify that all redirect routes and the default route are dropped.
6781 output = check_output('ip -6 address show dev veth-peer scope link')
6782 veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
6783 print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
6784 check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
9dcdf16b 6785 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
01420b2d 6786 self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
9944629e 6787
3b4eeccd
YW
6788 # Check if sd-radv refuses RS from the same interface.
6789 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
6790 since = datetime.datetime.now()
6791 check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
6792 self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
6793
a0430b0d
YW
6794 def check_ndisc_mtu(self, mtu):
6795 for _ in range(20):
6796 output = read_ipv6_sysctl_attr('veth99', 'mtu')
6797 if output == f'{mtu}':
6798 break
6799 time.sleep(0.5)
6800 else:
6801 self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
6802
6803 def test_ndisc_mtu(self):
6804 if not os.path.exists(test_ndisc_send):
6805 self.skipTest(f"{test_ndisc_send} does not exist.")
6806
6807 copy_network_unit('25-veth.netdev',
6808 '25-veth-peer-no-address.network',
6809 '25-ipv6-prefix-veth-token-static.network')
6810 start_networkd()
6811 self.wait_online('veth-peer:degraded')
6812
45c2bbba 6813 self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
a0430b0d
YW
6814
6815 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
6816 self.check_ndisc_mtu(1400)
6817
6818 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
6819 self.check_ndisc_mtu(1410)
6820
6821 check_output('ip link set dev veth99 mtu 1600')
7876f3d6 6822 check_output('ip link set dev veth-peer mtu 1600')
a0430b0d
YW
6823 self.check_ndisc_mtu(1410)
6824
6825 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
6826 self.check_ndisc_mtu(1600)
6827
6828 check_output('ip link set dev veth99 mtu 1800')
7876f3d6 6829 check_output('ip link set dev veth-peer mtu 1800')
a0430b0d
YW
6830 self.check_ndisc_mtu(1700)
6831
68248f43 6832 def test_ipv6_token_prefixstable(self):
a962d857 6833 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
c24c83dc 6834 start_networkd()
95e1fbba 6835 self.wait_online('veth99:routable', 'veth-peer:degraded')
c24c83dc 6836
ce4ed0ad 6837 output = check_output('ip -6 address show dev veth99')
c24c83dc 6838 print(output)
ce4ed0ad
YW
6839 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6840 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6841
6842 with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
6843 f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
6844
6845 networkctl_reload()
6846 self.wait_online('veth99:routable')
6847
6848 output = check_output('ip -6 address show dev veth99')
6849 print(output)
6850 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6851 self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6852
6853 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
6854 check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
6855
6856 networkctl_reconfigure('veth99')
6857 self.wait_online('veth99:routable')
8843726a
YW
6858 self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6859 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
ce4ed0ad
YW
6860
6861 check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
6862 check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
6863
6864 networkctl_reconfigure('veth99')
6865 self.wait_online('veth99:routable')
8843726a
YW
6866 self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable
6867 self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6868 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
c24c83dc 6869
68248f43 6870 def test_ipv6_token_prefixstable_without_address(self):
a962d857 6871 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
87bbebea 6872 start_networkd()
95e1fbba 6873 self.wait_online('veth99:routable', 'veth-peer:degraded')
87bbebea 6874
10d670a3 6875 output = networkctl_status('veth99')
87bbebea 6876 print(output)
7a2e124b
YW
6877 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
6878 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
87bbebea 6879
29fbbb13
YW
6880 def test_router_hop_limit(self):
6881 copy_network_unit('25-veth-client.netdev',
6882 '25-veth-router.netdev',
6883 '26-bridge.netdev',
6884 '25-veth-bridge.network',
6885 '25-veth-client.network',
6886 '25-veth-router-hop-limit.network',
6887 '25-bridge99.network')
6888 start_networkd()
2e73aa50 6889 self.wait_online('client:routable', 'client-p:enslaved',
29fbbb13
YW
6890 'router:degraded', 'router-p:enslaved',
6891 'bridge99:routable')
6892
2e73aa50 6893 self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
29fbbb13
YW
6894
6895 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
6896 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
6897
6898 networkctl_reload()
6899
2e73aa50
YW
6900 for _ in range(20):
6901 output = read_ipv6_sysctl_attr('client', 'hop_limit')
6902 if output == '43':
6903 break
6904 time.sleep(0.5)
6905
6906 self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
29fbbb13 6907
16ccdc37
YW
6908 def check_router_preference(self, suffix, metric_1, preference_1, metric_2, preference_2):
6909 self.wait_online('client:routable')
6910 self.wait_address('client', f'2002:da8:1:99:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6911 self.wait_address('client', f'2002:da8:1:98:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6912 self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1}', ipv='-6', timeout_sec=10)
6913 self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2}', ipv='-6', timeout_sec=10)
6914
6915 print('### ip -6 route show dev client default')
6916 output = check_output('ip -6 route show dev client default')
6917 print(output)
6918 self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1} expires [0-9]*sec pref {preference_1}')
6919 self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2} expires [0-9]*sec pref {preference_2}')
6920
6921 for i in [100, 200, 300, 512, 1024, 2048]:
6922 if i not in [metric_1, metric_2]:
2b397d43 6923 self.assertNotIn(f'metric {i} ', output)
16ccdc37
YW
6924
6925 for i in ['low', 'medium', 'high']:
6926 if i not in [preference_1, preference_2]:
2b397d43 6927 self.assertNotIn(f'pref {i}', output)
16ccdc37 6928
bb80f633
YW
6929 def test_router_preference(self):
6930 copy_network_unit('25-veth-client.netdev',
6931 '25-veth-router-high.netdev',
6932 '25-veth-router-low.netdev',
6933 '26-bridge.netdev',
6934 '25-veth-bridge.network',
6935 '25-veth-client.network',
6936 '25-veth-router-high.network',
6937 '25-veth-router-low.network',
6938 '25-bridge99.network')
6939 start_networkd()
95e1fbba
YW
6940 self.wait_online('client-p:enslaved',
6941 'router-high:degraded', 'router-high-p:enslaved',
6942 'router-low:degraded', 'router-low-p:enslaved',
6943 'bridge99:routable')
bb80f633
YW
6944
6945 networkctl_reconfigure('client')
95e1fbba 6946 self.wait_online('client:routable')
16ccdc37 6947 self.check_router_preference('00', 512, 'high', 2048, 'low')
bb80f633 6948
16ccdc37 6949 # change the map from preference to metric.
bb80f633
YW
6950 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
6951 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
bb80f633 6952 networkctl_reload()
16ccdc37 6953 self.check_router_preference('01', 100, 'high', 300, 'low')
bb80f633 6954
9fbab82b 6955 # swap the preference (for issue #28439)
16ccdc37
YW
6956 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6957 f.write('\n[IPv6SendRA]\nRouterPreference=low\n')
6958 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6959 f.write('\n[IPv6SendRA]\nRouterPreference=high\n')
9fbab82b 6960 networkctl_reload()
16ccdc37 6961 self.check_router_preference('01', 300, 'low', 100, 'high')
9fbab82b 6962
0f8afaf9 6963 # Use the same preference, and check if the two routes are not coalesced. See issue #33470.
16ccdc37 6964 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
0f8afaf9 6965 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
16ccdc37 6966 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
0f8afaf9
YW
6967 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
6968 networkctl_reload()
16ccdc37 6969 self.check_router_preference('01', 200, 'medium', 200, 'medium')
0f8afaf9 6970
c295b558
YW
6971 # Use route options to configure default routes.
6972 # The preference specified in the RA header should be ignored. See issue #33468.
6973 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6974 f.write('\n[IPv6SendRA]\nRouterPreference=high\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6975 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6976 f.write('\n[IPv6SendRA]\nRouterPreference=low\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6977 networkctl_reload()
6978 self.check_router_preference('01', 200, 'medium', 200, 'medium')
6979
6980 # Set zero lifetime to the route options.
6981 # The preference specified in the RA header should be used.
6982 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6983 f.write('LifetimeSec=0\n')
6984 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6985 f.write('LifetimeSec=0\n')
6986 networkctl_reload()
6987 self.check_router_preference('01', 100, 'high', 300, 'low')
6988
60ce6a34
YW
6989 # Use route options with preference to configure default routes.
6990 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6991 f.write('LifetimeSec=1200\nPreference=low\n')
6992 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6993 f.write('LifetimeSec=1200\nPreference=high\n')
6994 networkctl_reload()
6995 self.check_router_preference('01', 300, 'low', 100, 'high')
6996
6997 # Set zero lifetime again to the route options.
6998 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6999 f.write('LifetimeSec=0\n')
7000 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
7001 f.write('LifetimeSec=0\n')
7002 networkctl_reload()
7003 self.check_router_preference('01', 100, 'high', 300, 'low')
7004
0f8afaf9
YW
7005 def _test_ndisc_vs_static_route(self, manage_foreign_nexthops):
7006 if not manage_foreign_nexthops:
7007 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
62fb079a
YW
7008 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-static-route.network')
7009 start_networkd()
7010 self.wait_online('veth99:routable', 'veth-peer:degraded')
7011
62fb079a 7012 # If a conflicting static route is already configured, do not override the static route.
d7b323c2
YW
7013 print('### ip -6 route show dev veth99 default')
7014 output = check_output('ip -6 route show dev veth99 default')
62fb079a 7015 print(output)
d7b323c2 7016 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
0f8afaf9
YW
7017 if manage_foreign_nexthops:
7018 self.assertRegex(output, r'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium')
7019 else:
7020 self.assertNotIn('proto ra', output)
7021
7022 print('### ip -6 nexthop show dev veth99')
7023 output = check_output('ip -6 nexthop show dev veth99')
7024 print(output)
7025 if manage_foreign_nexthops:
7026 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra')
7027 else:
7028 self.assertEqual(output, '')
62fb079a 7029
62fb079a 7030 # Also check if the static route is protected from RA with zero lifetime
d7b323c2
YW
7031 with open(os.path.join(network_unit_dir, '25-ipv6-prefix.network'), mode='a', encoding='utf-8') as f:
7032 f.write('\n[Network]\nIPv6SendRA=no\n')
7033 networkctl_reload() # This makes veth-peer being reconfigured, and send RA with zero lifetime
0f8afaf9 7034 self.wait_route_dropped('veth99', r'default (nhid [0-9]* |)via fe80::1034:56ff:fe78:9abd proto ra metric 256', ipv='-6', timeout_sec=10)
d7b323c2
YW
7035
7036 print('### ip -6 route show dev veth99 default')
7037 output = check_output('ip -6 route show dev veth99 default')
62fb079a 7038 print(output)
d7b323c2
YW
7039 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
7040 self.assertNotIn('proto ra', output)
62fb079a 7041
0f8afaf9
YW
7042 # Check if nexthop is removed.
7043 print('### ip -6 nexthop show dev veth99')
7044 output = check_output('ip -6 nexthop show dev veth99')
7045 print(output)
7046 self.assertEqual(output, '')
7047
7048 def test_ndisc_vs_static_route(self):
7049 first = True
7050 for manage_foreign_nexthops in [True, False]:
7051 if first:
7052 first = False
7053 else:
7054 self.tearDown()
7055
7056 print(f'### test_ndisc_vs_static_route(manage_foreign_nexthops={manage_foreign_nexthops})')
7057 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
7058 self._test_ndisc_vs_static_route(manage_foreign_nexthops)
7059
4f6d8ab0
YW
7060 # radvd supports captive portal since v2.20.
7061 # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc
c1dd58b3
FS
7062 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7063 def test_captive_portal(self):
7064 copy_network_unit('25-veth-client.netdev',
7065 '25-veth-router-captive.netdev',
7066 '26-bridge.netdev',
7067 '25-veth-client-captive.network',
7068 '25-veth-router-captive.network',
7069 '25-veth-bridge-captive.network',
7070 '25-bridge99.network')
7071 start_networkd()
95e1fbba
YW
7072 self.wait_online('bridge99:routable', 'client-p:enslaved',
7073 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
7074
7075 start_radvd(config_file='captive-portal.conf')
7076 networkctl_reconfigure('client')
95e1fbba 7077 self.wait_online('client:routable')
c1dd58b3
FS
7078
7079 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 7080 output = networkctl_status('client')
c1dd58b3
FS
7081 print(output)
7082 self.assertIn('Captive Portal: http://systemd.io', output)
7083
7084 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7085 def test_invalid_captive_portal(self):
7086 def radvd_write_config(captive_portal_uri):
7087 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
7088 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
7089
7090 captive_portal_uris = [
7091 "42ěščěškd ěšč ě s",
7092 " ",
7093 "🤔",
7094 ]
7095
7096 copy_network_unit('25-veth-client.netdev',
7097 '25-veth-router-captive.netdev',
7098 '26-bridge.netdev',
7099 '25-veth-client-captive.network',
7100 '25-veth-router-captive.network',
7101 '25-veth-bridge-captive.network',
7102 '25-bridge99.network')
7103 start_networkd()
95e1fbba
YW
7104 self.wait_online('bridge99:routable', 'client-p:enslaved',
7105 'router-captive:degraded', 'router-captivep:enslaved')
c1dd58b3
FS
7106
7107 for uri in captive_portal_uris:
7108 print(f"Captive portal: {uri}")
7109 radvd_write_config(uri)
7110 stop_radvd()
7111 start_radvd(config_file='bogus-captive-portal.conf')
7112 networkctl_reconfigure('client')
95e1fbba 7113 self.wait_online('client:routable')
c1dd58b3
FS
7114
7115 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
10d670a3 7116 output = networkctl_status('client')
c1dd58b3
FS
7117 print(output)
7118 self.assertNotIn('Captive Portal:', output)
7119
be68c2c9 7120class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
1f0e3109
SS
7121
7122 def setUp(self):
a962d857 7123 setup_common()
1f0e3109
SS
7124
7125 def tearDown(self):
a962d857 7126 tear_down_common()
1f0e3109 7127
009d64dd 7128 def check_dhcp_server(self, persist_leases='yes'):
10d670a3 7129 output = networkctl_status('veth99')
c5f7a087 7130 print(output)
e6c4b5dc 7131 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
c5f7a087
YW
7132 self.assertIn('Gateway: 192.168.5.3', output)
7133 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
7134 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
7135
10d670a3 7136 output = networkctl_status('veth-peer')
1c4411b7 7137 print(output)
8bdece74
FS
7138 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
7139
009d64dd
YW
7140 if persist_leases == 'yes':
7141 path = '/var/lib/systemd/network/dhcp-server-lease/veth-peer'
7142 elif persist_leases == 'runtime':
7143 path = '/run/systemd/netif/dhcp-server-lease/veth-peer'
bc91875a 7144 else:
009d64dd
YW
7145 path = None
7146
7147 if path:
7148 with open(path, encoding='utf-8') as f:
7149 check_json(f.read())
bc91875a
YW
7150
7151 def test_dhcp_server(self):
7152 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7153 start_networkd()
7154 self.wait_online('veth99:routable', 'veth-peer:routable')
7155
7156 self.check_dhcp_server()
7157
1c4411b7
YW
7158 networkctl_reconfigure('veth-peer')
7159 self.wait_online('veth-peer:routable')
7160
7161 for _ in range(10):
7162 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
7163 if 'Offered DHCP leases: 192.168.5.' in output:
7164 break
7165 time.sleep(.2)
7166 else:
7167 self.fail()
7168
bc91875a
YW
7169 def test_dhcp_server_persist_leases_no(self):
7170 copy_networkd_conf_dropin('persist-leases-no.conf')
7171 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7172 start_networkd()
7173 self.wait_online('veth99:routable', 'veth-peer:routable')
7174
009d64dd 7175 self.check_dhcp_server(persist_leases='no')
bc91875a
YW
7176
7177 remove_networkd_conf_dropin('persist-leases-no.conf')
7178 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7179 f.write('[DHCPServer]\nPersistLeases=no')
7180 restart_networkd()
7181 self.wait_online('veth99:routable', 'veth-peer:routable')
7182
009d64dd
YW
7183 self.check_dhcp_server(persist_leases='no')
7184
7185 def test_dhcp_server_persist_leases_runtime(self):
7186 copy_networkd_conf_dropin('persist-leases-runtime.conf')
7187 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7188 start_networkd()
7189 self.wait_online('veth99:routable', 'veth-peer:routable')
7190
7191 self.check_dhcp_server(persist_leases='runtime')
7192
7193 remove_networkd_conf_dropin('persist-leases-runtime.conf')
7194 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7195 f.write('[DHCPServer]\nPersistLeases=runtime')
7196 restart_networkd()
7197 self.wait_online('veth99:routable', 'veth-peer:routable')
7198
7199 self.check_dhcp_server(persist_leases='runtime')
bc91875a 7200
47f1ce16
YW
7201 def test_dhcp_server_null_server_address(self):
7202 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
7203 start_networkd()
95e1fbba 7204 self.wait_online('veth99:routable', 'veth-peer:routable')
47f1ce16
YW
7205
7206 output = check_output('ip --json address show dev veth-peer')
7207 server_address = json.loads(output)[0]['addr_info'][0]['local']
7208 print(server_address)
7209
7210 output = check_output('ip --json address show dev veth99')
7211 client_address = json.loads(output)[0]['addr_info'][0]['local']
7212 print(client_address)
7213
10d670a3 7214 output = networkctl_status('veth99')
47f1ce16 7215 print(output)
e6c4b5dc 7216 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
47f1ce16
YW
7217 self.assertIn(f'Gateway: {server_address}', output)
7218 self.assertIn(f'DNS: {server_address}', output)
7219 self.assertIn(f'NTP: {server_address}', output)
7220
10d670a3 7221 output = networkctl_status('veth-peer')
47f1ce16
YW
7222 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7223
93126bb0
YW
7224 # Check if the same addresses are used even if the service is restarted.
7225 restart_networkd()
7226 self.wait_online('veth99:routable', 'veth-peer:routable')
7227
7228 output = check_output('ip -4 address show dev veth-peer')
7229 print(output)
7230 self.assertIn(f'{server_address}', output)
7231
7232 output = check_output('ip -4 address show dev veth99')
7233 print(output)
7234 self.assertIn(f'{client_address}', output)
7235
7236 output = networkctl_status('veth99')
7237 print(output)
e6c4b5dc 7238 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
93126bb0
YW
7239 self.assertIn(f'Gateway: {server_address}', output)
7240 self.assertIn(f'DNS: {server_address}', output)
7241 self.assertIn(f'NTP: {server_address}', output)
7242
7243 output = networkctl_status('veth-peer')
7244 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7245
c5f7a087 7246 def test_dhcp_server_with_uplink(self):
a962d857
YW
7247 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
7248 '12-dummy.netdev', '25-dhcp-server-uplink.network')
2cf6fdff 7249 start_networkd()
95e1fbba 7250 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 7251
10d670a3 7252 output = networkctl_status('veth99')
1f0e3109 7253 print(output)
e6c4b5dc 7254 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
32d97330
YW
7255 self.assertIn('Gateway: 192.168.5.3', output)
7256 self.assertIn('DNS: 192.168.5.1', output)
7257 self.assertIn('NTP: 192.168.5.1', output)
1f0e3109 7258
1f0e3109 7259 def test_emit_router_timezone(self):
a962d857 7260 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
2cf6fdff 7261 start_networkd()
95e1fbba 7262 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 7263
10d670a3 7264 output = networkctl_status('veth99')
1f0e3109 7265 print(output)
e6c4b5dc 7266 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
32d97330
YW
7267 self.assertIn('Gateway: 192.168.5.1', output)
7268 self.assertIn('Time Zone: Europe/Berlin', output)
1f0e3109 7269
5e5fe058 7270 def test_dhcp_server_static_lease_mac_by_network(self):
a962d857 7271 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5e5fe058 7272 copy_networkd_conf_dropin('10-dhcp-client-id-duid.conf')
ffaece68 7273 start_networkd()
95e1fbba 7274 self.wait_online('veth99:routable', 'veth-peer:routable')
ffaece68 7275
10d670a3 7276 output = networkctl_status('veth99')
ffaece68 7277 print(output)
e6c4b5dc
SP
7278 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7279 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
82c60c93 7280
5e5fe058
YW
7281 def test_dhcp_server_static_lease_mac_by_global(self):
7282 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7283 copy_networkd_conf_dropin('10-dhcp-client-id-mac.conf')
7284 start_networkd()
7285 self.wait_online('veth99:routable', 'veth-peer:routable')
7286
7287 output = networkctl_status('veth99')
7288 print(output)
7289 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7290 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
7291
7292 def test_dhcp_server_static_lease_duid(self):
82c60c93
YW
7293 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7294 start_networkd()
95e1fbba 7295 self.wait_online('veth99:routable', 'veth-peer:routable')
82c60c93 7296
10d670a3 7297 output = networkctl_status('veth99')
82c60c93 7298 print(output)
e6c4b5dc
SP
7299 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7300 self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
ffaece68 7301
c95df587 7302class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
c95df587
YA
7303
7304 def setUp(self):
a962d857 7305 setup_common()
c95df587
YA
7306
7307 def tearDown(self):
a962d857 7308 tear_down_common()
c95df587
YA
7309
7310 def test_relay_agent(self):
a962d857
YW
7311 copy_network_unit('25-agent-veth-client.netdev',
7312 '25-agent-veth-server.netdev',
7313 '25-agent-client.network',
7314 '25-agent-server.network',
7315 '25-agent-client-peer.network',
7316 '25-agent-server-peer.network')
c95df587
YA
7317 start_networkd()
7318
95e1fbba 7319 self.wait_online('client:routable')
c95df587 7320
10d670a3 7321 output = networkctl_status('client')
c95df587 7322 print(output)
e6c4b5dc 7323 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)')
c95df587 7324
c4047829 7325 def test_relay_agent_on_bridge(self):
a663ddc0
YW
7326 copy_network_unit('25-agent-bridge.netdev',
7327 '25-agent-veth-client.netdev',
7328 '25-agent-bridge.network',
7329 '25-agent-bridge-port.network',
7330 '25-agent-client.network')
7331 start_networkd()
95e1fbba 7332 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
a663ddc0
YW
7333
7334 # For issue #30763.
45c2bbba 7335 self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
a663ddc0 7336
be68c2c9 7337class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
1f0e3109
SS
7338
7339 def setUp(self):
a962d857 7340 setup_common()
1f0e3109
SS
7341
7342 def tearDown(self):
a962d857 7343 tear_down_common()
1f0e3109
SS
7344
7345 def test_dhcp_client_ipv6_only(self):
a962d857 7346 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
1f0e3109 7347
2cf6fdff 7348 start_networkd()
95e1fbba 7349 self.wait_online('veth-peer:carrier')
2d7ca6b4
YW
7350
7351 # information request mode
46f2eb51
YW
7352 # The name ipv6-only option may not be supported by older dnsmasq
7353 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
7354 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7355 '--dhcp-option=option6:dns-server,[2600::ee]',
2d7ca6b4
YW
7356 '--dhcp-option=option6:ntp-server,[2600::ff]',
7357 ra_mode='ra-stateless')
95e1fbba 7358 self.wait_online('veth99:routable', 'veth-peer:routable')
2d7ca6b4 7359
0f9efffa
YW
7360 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
7361 # Let's wait for the expected DNS server being listed in the state file.
7362 for _ in range(100):
7363 output = read_link_state_file('veth99')
0f9efffa
YW
7364 if 'DNS=2600::ee' in output:
7365 break
7366 time.sleep(.2)
7367
2d7ca6b4
YW
7368 # Check link state file
7369 print('## link state file')
7370 output = read_link_state_file('veth99')
7371 print(output)
7372 self.assertIn('DNS=2600::ee', output)
7373 self.assertIn('NTP=2600::ff', output)
7374
7375 # Check manager state file
7376 print('## manager state file')
7377 output = read_manager_state_file()
7378 print(output)
7379 self.assertRegex(output, 'DNS=.*2600::ee')
7380 self.assertRegex(output, 'NTP=.*2600::ff')
7381
7382 print('## dnsmasq log')
7383 output = read_dnsmasq_log_file()
7384 print(output)
7385 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7386 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
7387 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7388 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7389 self.assertNotIn('DHCPREPLY(veth-peer)', output)
7390
3aa47694 7391 # Check json format
10d670a3 7392 check_json(networkctl_json('veth99'))
3aa47694 7393
2d7ca6b4
YW
7394 # solicit mode
7395 stop_dnsmasq()
46f2eb51
YW
7396 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7397 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 7398 '--dhcp-option=option6:ntp-server,[2600::ff]')
2d7ca6b4 7399 networkctl_reconfigure('veth99')
95e1fbba 7400 self.wait_online('veth99:routable', 'veth-peer:routable')
1f0e3109 7401
18f2638f
YW
7402 # checking address
7403 output = check_output('ip address show dev veth99 scope global')
c5fcd8a7 7404 print(output)
18f2638f
YW
7405 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7406 self.assertNotIn('192.168.5', output)
c5fcd8a7 7407
589af70b
YW
7408 # checking semi-static route
7409 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7410 print(output)
7411 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7412
3a956d38 7413 # Confirm that ipv6 token is not set in the kernel
371810d1 7414 output = check_output('ip token show dev veth99')
3a956d38
YW
7415 print(output)
7416 self.assertRegex(output, 'token :: dev veth99')
7417
0f9efffa 7418 # Make manager and link state file updated
10d670a3 7419 resolvectl('revert', 'veth99')
0f9efffa 7420
34290c6a
YW
7421 # Check link state file
7422 print('## link state file')
7423 output = read_link_state_file('veth99')
7424 print(output)
7425 self.assertIn('DNS=2600::ee', output)
7426 self.assertIn('NTP=2600::ff', output)
7427
7428 # Check manager state file
7429 print('## manager state file')
7430 output = read_manager_state_file()
7431 print(output)
7432 self.assertRegex(output, 'DNS=.*2600::ee')
7433 self.assertRegex(output, 'NTP=.*2600::ff')
7434
91a7afde
YW
7435 print('## dnsmasq log')
7436 output = read_dnsmasq_log_file()
7437 print(output)
2d7ca6b4 7438 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
7439 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7440 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7441 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7442 self.assertIn('DHCPREPLY(veth-peer)', output)
7443 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7444
3aa47694 7445 # Check json format
10d670a3 7446 check_json(networkctl_json('veth99'))
3aa47694
YW
7447
7448 # Testing without rapid commit support
91a7afde
YW
7449 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
7450 f.write('\n[DHCPv6]\nRapidCommit=no\n')
7451
7452 stop_dnsmasq()
46f2eb51
YW
7453 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7454 '--dhcp-option=option6:dns-server,[2600::ee]',
34290c6a 7455 '--dhcp-option=option6:ntp-server,[2600::ff]')
91a7afde
YW
7456
7457 networkctl_reload()
95e1fbba 7458 self.wait_online('veth99:routable', 'veth-peer:routable')
91a7afde
YW
7459
7460 # checking address
7461 output = check_output('ip address show dev veth99 scope global')
7462 print(output)
7463 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7464 self.assertNotIn('192.168.5', output)
7465
7466 # checking semi-static route
7467 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7468 print(output)
7469 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7470
0f9efffa 7471 # Make manager and link state file updated
10d670a3 7472 resolvectl('revert', 'veth99')
0f9efffa 7473
34290c6a
YW
7474 # Check link state file
7475 print('## link state file')
7476 output = read_link_state_file('veth99')
7477 print(output)
7478 self.assertIn('DNS=2600::ee', output)
7479 self.assertIn('NTP=2600::ff', output)
7480
7481 # Check manager state file
7482 print('## manager state file')
7483 output = read_manager_state_file()
7484 print(output)
7485 self.assertRegex(output, 'DNS=.*2600::ee')
7486 self.assertRegex(output, 'NTP=.*2600::ff')
7487
91a7afde
YW
7488 print('## dnsmasq log')
7489 output = read_dnsmasq_log_file()
7490 print(output)
2d7ca6b4 7491 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
91a7afde
YW
7492 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7493 self.assertIn('DHCPADVERTISE(veth-peer)', output)
7494 self.assertIn('DHCPREQUEST(veth-peer)', output)
7495 self.assertIn('DHCPREPLY(veth-peer)', output)
7496 self.assertNotIn('rapid-commit', output)
7497
3aa47694 7498 # Check json format
10d670a3 7499 check_json(networkctl_json('veth99'))
3aa47694 7500
e1ef7771 7501 def test_dhcp_client_ipv6_dbus_status(self):
e1ef7771 7502 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
e1ef7771 7503 start_networkd()
95e1fbba 7504 self.wait_online('veth-peer:carrier')
e1ef7771 7505
7506 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
fcdd21ec 7507 # bit set) has yet been received and the configuration does not include WithoutRA=true
e081ffc1 7508 state = get_dhcp6_client_state('veth99')
46f2eb51 7509 print(f"DHCPv6 client state = {state}")
e1ef7771 7510 self.assertEqual(state, 'stopped')
7511
46f2eb51
YW
7512 state = get_dhcp4_client_state('veth99')
7513 print(f"DHCPv4 client state = {state}")
7514 self.assertEqual(state, 'selecting')
7515
7516 start_dnsmasq('--dhcp-option=108,00:00:02:00')
95e1fbba 7517 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 7518
e081ffc1 7519 state = get_dhcp6_client_state('veth99')
46f2eb51 7520 print(f"DHCPv6 client state = {state}")
e1ef7771 7521 self.assertEqual(state, 'bound')
7522
46f2eb51
YW
7523 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
7524 for _ in range(100):
7525 state = get_dhcp4_client_state('veth99')
7526 if state == 'stopped':
7527 break
7528 time.sleep(.2)
7529
7530 print(f"DHCPv4 client state = {state}")
7531 self.assertEqual(state, 'stopped')
7532
aa7336f1
YW
7533 # restart dnsmasq to clear log
7534 stop_dnsmasq()
7535 start_dnsmasq('--dhcp-option=108,00:00:02:00')
7536
7537 # Test renew command
7538 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
10d670a3 7539 networkctl('renew', 'veth99')
aa7336f1
YW
7540
7541 for _ in range(100):
7542 state = get_dhcp4_client_state('veth99')
7543 if state == 'stopped':
7544 break
7545 time.sleep(.2)
7546
7547 print(f"DHCPv4 client state = {state}")
7548 self.assertEqual(state, 'stopped')
7549
7550 print('## dnsmasq log')
7551 output = read_dnsmasq_log_file()
7552 print(output)
7553 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
7554 self.assertIn('DHCPOFFER(veth-peer)', output)
7555 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7556 self.assertNotIn('DHCPACK(veth-peer)', output)
7557
e448fcd0
SS
7558 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
7559 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
7560
7561 start_networkd()
95e1fbba 7562 self.wait_online('veth-peer:carrier')
e448fcd0 7563 start_dnsmasq()
95e1fbba 7564 self.wait_online('veth99:routable', 'veth-peer:routable')
e448fcd0
SS
7565
7566 # checking address
7567 output = check_output('ip address show dev veth99 scope global')
7568 print(output)
7569 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7570 self.assertNotIn('192.168.5', output)
7571
7572 print('## dnsmasq log')
7573 output = read_dnsmasq_log_file()
7574 print(output)
7575 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
7576 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7577 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7578 self.assertIn('DHCPREPLY(veth-peer)', output)
7579 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7580
d22f2fb9 7581 @expectedFailureIfKernelReturnsInvalidFlags()
1f0e3109 7582 def test_dhcp_client_ipv4_only(self):
80f38c1f
YW
7583 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network',
7584 '25-sit-dhcp4.netdev', '25-sit-dhcp4.network')
1f0e3109 7585
c742d7e8
TM
7586 self.setup_nftset('addr4', 'ipv4_addr')
7587 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
7588 self.setup_nftset('ifindex', 'iface_index')
7589
2cf6fdff 7590 start_networkd()
95e1fbba 7591 self.wait_online('veth-peer:carrier')
a962d857 7592 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
b5c8f471 7593 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
a962d857
YW
7594 '--dhcp-option=option:domain-search,example.com',
7595 '--dhcp-alternate-port=67,5555',
7596 ipv4_range='192.168.5.110,192.168.5.119')
80f38c1f 7597 self.wait_online('veth99:routable', 'veth-peer:routable', 'sit-dhcp4:carrier')
9e7d91ed 7598 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
1f0e3109 7599
18f2638f
YW
7600 print('## ip address show dev veth99 scope global')
7601 output = check_output('ip address show dev veth99 scope global')
1f0e3109 7602 print(output)
18f2638f
YW
7603 self.assertIn('mtu 1492', output)
7604 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
7605 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')
7606 self.assertNotIn('2600::', output)
195a18c1 7607
6b524d70
YW
7608 output = check_output('ip -4 --json address show dev veth99')
7609 for i in json.loads(output)[0]['addr_info']:
7610 if i['label'] == 'test-label':
7611 address1 = i['local']
7612 break
7613 else:
7614 self.assertFalse(True)
7615
7616 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
7617
18f2638f
YW
7618 print('## ip route show table main dev veth99')
7619 output = check_output('ip route show table main dev veth99')
195a18c1 7620 print(output)
18f2638f
YW
7621 # no DHCP routes assigned to the main table
7622 self.assertNotIn('proto dhcp', output)
7623 # static routes
7624 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7625 self.assertIn('192.168.5.0/24 proto static scope link', output)
7626 self.assertIn('192.168.6.0/24 proto static scope link', output)
7627 self.assertIn('192.168.7.0/24 proto static scope link', output)
7628
7629 print('## ip route show table 211 dev veth99')
7630 output = check_output('ip route show table 211 dev veth99')
7631 print(output)
6b524d70
YW
7632 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
7633 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7634 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7635 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
7636 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
2ea15435
YW
7637 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1}')
7638
7639 print('## ip route show table 212 dev veth99')
7640 output = check_output('ip route show table 212 dev veth99')
7641 print(output)
7642 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7643 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7644 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1}')
18f2638f 7645
1a8e1d78
YW
7646 print('## link state file')
7647 output = read_link_state_file('veth99')
7648 print(output)
b5c8f471 7649 # checking DNS server, SIP server, and Domains
1a8e1d78 7650 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
b5c8f471 7651 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
1a8e1d78 7652 self.assertIn('DOMAINS=example.com', output)
195a18c1 7653
b5c8f471 7654 print('## json')
10d670a3 7655 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
7656
7657 self.assertEqual(len(j['DNS']), 2)
7658 for i in j['DNS']:
7659 print(i)
7660 self.assertEqual(i['Family'], 2)
7661 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7662 self.assertRegex(a, '^192.168.5.[67]$')
7663 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7664 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7665 self.assertEqual('192.168.5.1', a)
7666
7667 self.assertEqual(len(j['SIP']), 2)
7668 for i in j['SIP']:
7669 print(i)
7670 self.assertEqual(i['Family'], 2)
7671 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7672 self.assertRegex(a, '^192.168.5.2[12]$')
7673 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7674 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7675 self.assertEqual('192.168.5.1', a)
7676
80f38c1f
YW
7677 print('## tunnel')
7678 output = check_output('ip -d link show sit-dhcp4')
7679 print(output)
7680 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address1} dev veth99')
7681
18f2638f 7682 print('## dnsmasq log')
063c7e9b
YW
7683 output = read_dnsmasq_log_file()
7684 print(output)
7685 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 7686 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
063c7e9b
YW
7687 self.assertIn('client provides name: test-hostname', output)
7688 self.assertIn('26:mtu', output)
18f2638f
YW
7689
7690 # change address range, DNS servers, and Domains
888f57c1 7691 stop_dnsmasq()
a962d857 7692 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
b5c8f471 7693 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
a962d857
YW
7694 '--dhcp-option=option:domain-search,foo.example.com',
7695 '--dhcp-alternate-port=67,5555',
7696 ipv4_range='192.168.5.120,192.168.5.129',)
195a18c1
YW
7697
7698 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c 7699 print('Wait for the DHCP lease to be expired')
6b524d70 7700 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
4b31fc88 7701 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
195a18c1 7702
95e1fbba 7703 self.wait_online('veth99:routable', 'veth-peer:routable')
195a18c1 7704
18f2638f
YW
7705 print('## ip address show dev veth99 scope global')
7706 output = check_output('ip address show dev veth99 scope global')
195a18c1 7707 print(output)
18f2638f
YW
7708 self.assertIn('mtu 1492', output)
7709 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6b524d70 7710 self.assertNotIn(f'{address1}', output)
18f2638f
YW
7711 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')
7712 self.assertNotIn('2600::', output)
195a18c1 7713
6b524d70
YW
7714 output = check_output('ip -4 --json address show dev veth99')
7715 for i in json.loads(output)[0]['addr_info']:
7716 if i['label'] == 'test-label':
7717 address2 = i['local']
7718 break
7719 else:
7720 self.assertFalse(True)
7721
7722 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
7723
18f2638f
YW
7724 print('## ip route show table main dev veth99')
7725 output = check_output('ip route show table main dev veth99')
195a18c1 7726 print(output)
18f2638f
YW
7727 # no DHCP routes assigned to the main table
7728 self.assertNotIn('proto dhcp', output)
7729 # static routes
7730 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7731 self.assertIn('192.168.5.0/24 proto static scope link', output)
7732 self.assertIn('192.168.6.0/24 proto static scope link', output)
7733 self.assertIn('192.168.7.0/24 proto static scope link', output)
7734
7735 print('## ip route show table 211 dev veth99')
7736 output = check_output('ip route show table 211 dev veth99')
7737 print(output)
6b524d70
YW
7738 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
7739 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7740 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
18f2638f 7741 self.assertNotIn('192.168.5.6', output)
6b524d70
YW
7742 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
7743 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
2ea15435
YW
7744 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2}')
7745
7746 print('## ip route show table 212 dev veth99')
7747 output = check_output('ip route show table 212 dev veth99')
7748 print(output)
7749 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7750 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
7751 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2}')
18f2638f 7752
1a8e1d78
YW
7753 print('## link state file')
7754 output = read_link_state_file('veth99')
7755 print(output)
b5c8f471 7756 # checking DNS server, SIP server, and Domains
1a8e1d78 7757 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
b5c8f471 7758 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
1a8e1d78 7759 self.assertIn('DOMAINS=foo.example.com', output)
18f2638f 7760
b5c8f471 7761 print('## json')
10d670a3 7762 j = json.loads(networkctl_json('veth99'))
b5c8f471
YW
7763
7764 self.assertEqual(len(j['DNS']), 3)
7765 for i in j['DNS']:
7766 print(i)
7767 self.assertEqual(i['Family'], 2)
7768 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7769 self.assertRegex(a, '^192.168.5.[178]$')
7770 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7771 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7772 self.assertEqual('192.168.5.1', a)
7773
7774 self.assertEqual(len(j['SIP']), 2)
7775 for i in j['SIP']:
7776 print(i)
7777 self.assertEqual(i['Family'], 2)
7778 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7779 self.assertRegex(a, '^192.168.5.2[34]$')
7780 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7781 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7782 self.assertEqual('192.168.5.1', a)
7783
80f38c1f
YW
7784 print('## tunnel')
7785 output = check_output('ip -d link show sit-dhcp4')
7786 print(output)
7787 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address2} dev veth99')
7788
18f2638f 7789 print('## dnsmasq log')
063c7e9b
YW
7790 output = read_dnsmasq_log_file()
7791 print(output)
7792 self.assertIn('vendor class: FooBarVendorTest', output)
6b524d70 7793 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
063c7e9b
YW
7794 self.assertIn('client provides name: test-hostname', output)
7795 self.assertIn('26:mtu', output)
1f0e3109 7796
0677130e 7797 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
a4640bed 7798
c742d7e8
TM
7799 self.check_nftset('addr4', r'192\.168\.5\.1')
7800 self.check_nftset('network4', r'192\.168\.5\.0/24')
7801 self.check_nftset('ifindex', 'veth99')
7802
58a011ba
YW
7803 # Check if DHCPv4 address and routes are removed on stop. For issue #34837.
7804 stop_networkd(show_logs=False)
7805 self.wait_address_dropped('veth99', f'inet {address2}/24', ipv='-4', timeout_sec=120)
7806
7807 print('## ip address show dev veth99 scope global')
7808 output = check_output('ip address show dev veth99 scope global')
7809 print(output)
7810 self.assertNotIn(f'{address2}', output)
7811
7812 print('## ip route show table main dev veth99')
7813 output = check_output('ip route show table main dev veth99')
7814 print(output)
7815 self.assertNotIn(f'{address2}', output)
7816
7817 print('## ip route show table 211 dev veth99')
7818 output = check_output('ip route show table 211 dev veth99')
7819 print(output)
7820 self.assertNotIn(f'{address2}', output)
7821
2ea15435
YW
7822 print('## ip route show table 212 dev veth99')
7823 output = check_output('ip route show table 212 dev veth99')
7824 print(output)
7825 self.assertNotIn(f'{address2}', output)
7826
c742d7e8
TM
7827 self.teardown_nftset('addr4', 'network4', 'ifindex')
7828
e1ef7771 7829 def test_dhcp_client_ipv4_dbus_status(self):
e1ef7771 7830 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
e1ef7771 7831 start_networkd()
95e1fbba 7832 self.wait_online('veth-peer:carrier')
e1ef7771 7833
e081ffc1 7834 state = get_dhcp4_client_state('veth99')
e1ef7771 7835 print(f"State = {state}")
6b524d70 7836 self.assertEqual(state, 'rebooting')
e1ef7771 7837
7838 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
7839 '--dhcp-option=option:domain-search,example.com',
7840 '--dhcp-alternate-port=67,5555',
7841 ipv4_range='192.168.5.110,192.168.5.119')
95e1fbba 7842 self.wait_online('veth99:routable', 'veth-peer:routable')
e1ef7771 7843 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
7844
e081ffc1 7845 state = get_dhcp4_client_state('veth99')
e1ef7771 7846 print(f"State = {state}")
7847 self.assertEqual(state, 'bound')
7848
b65c5390
YW
7849 def test_dhcp_client_allow_list(self):
7850 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
7851
7852 start_networkd()
95e1fbba 7853 self.wait_online('veth-peer:carrier')
b65c5390
YW
7854 since = datetime.datetime.now()
7855 start_dnsmasq()
7856
45c2bbba 7857 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
b65c5390
YW
7858
7859 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
7860 since = datetime.datetime.now()
7861 networkctl_reload()
7862
45c2bbba 7863 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
b65c5390
YW
7864
7865 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
7866 since = datetime.datetime.now()
7867 networkctl_reload()
7868
45c2bbba 7869 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
b65c5390 7870
2beecc70
RP
7871 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
7872 def test_dhcp_client_rapid_commit(self):
7873 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
7874 start_networkd()
95e1fbba 7875 self.wait_online('veth-peer:carrier')
2beecc70
RP
7876
7877 start_dnsmasq('--dhcp-rapid-commit')
95e1fbba 7878 self.wait_online('veth99:routable', 'veth-peer:routable')
2beecc70
RP
7879 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7880
7881 state = get_dhcp4_client_state('veth99')
7882 print(f"DHCPv4 client state = {state}")
7883 self.assertEqual(state, 'bound')
7884
7885 output = read_dnsmasq_log_file()
7886 self.assertIn('DHCPDISCOVER(veth-peer)', output)
7887 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7888 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7889 self.assertIn('DHCPACK(veth-peer)', output)
7890
fb9076b9
AD
7891 def check_bootp_client(self, check_log):
7892 self.wait_online('veth99:routable', 'veth-peer:routable')
7893 output = check_output('ip -4 address show dev veth99')
7894 print(output)
7895 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24')
7896
7897 state = get_dhcp4_client_state('veth99')
7898 print(f"DHCPv4 client state = {state}")
7899 self.assertEqual(state, 'bound')
7900
7901 if check_log:
7902 output = read_dnsmasq_log_file()
7903 print(output)
7904 self.assertIn('BOOTP(veth-peer)', output)
7905 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7906 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7907 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7908 self.assertNotIn('DHCPACK(veth-peer)', output)
7909
7910 def test_bootp_client(self):
7911 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-bootp-client.network')
7912 start_networkd()
7913 self.wait_online('veth-peer:carrier')
7914 start_dnsmasq('--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule')
7915 self.check_bootp_client(check_log=True)
7916
7917 touch_network_unit('25-bootp-client.network')
7918 networkctl_reload()
7919 self.check_bootp_client(check_log=True)
7920
7921 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7922 f.write('[DHCPv4]\nBOOTP=no\n')
7923
7924 networkctl_reload()
7925 self.check_bootp_client(check_log=False)
7926
7927 output = read_dnsmasq_log_file()
7928 print(output)
7929 # Note, on reload, the DHCP client will be started from INIT-REBOOT state,
7930 # hence DISCOVER and OFFER message will not be sent/received.
7931 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7932 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7933 self.assertIn('DHCPREQUEST(veth-peer)', output)
7934 self.assertIn('DHCPACK(veth-peer)', output)
7935
7936 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7937 f.write('[DHCPv4]\nBOOTP=yes\n')
7938
7939 since = datetime.datetime.now()
7940
7941 networkctl_reload()
7942 self.check_bootp_client(check_log=False)
7943
7944 # Check if the client send RELEASE message of the previous lease
7945 self.check_networkd_log('veth99: DHCPv4 client: RELEASE', since=since)
7946
46f2eb51
YW
7947 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
7948 copy_network_unit('25-veth.netdev',
7949 '25-dhcp-server-ipv6-only-mode.network',
7950 '25-dhcp-client-ipv6-only-mode.network')
7951 start_networkd()
95e1fbba 7952 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
46f2eb51
YW
7953 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7954
7955 state = get_dhcp4_client_state('veth99')
7956 print(f"State = {state}")
7957 self.assertEqual(state, 'bound')
7958
7c0d36ff 7959 def test_dhcp_client_ipv4_use_routes_gateway(self):
a962d857 7960 first = True
e1220a70 7961 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
a962d857
YW
7962 if first:
7963 first = False
7964 else:
7965 self.tearDown()
7966
7967 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
7968 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
7969 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
7c0d36ff 7970
e1220a70 7971 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
1e86c833
DDM
7972 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
7973 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
6983bb0e
FS
7974 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
7975 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
7976 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
a962d857 7977 copy_network_unit(*testunits, copy_dropins=False)
4c2e1833
YW
7978
7979 start_networkd()
95e1fbba 7980 self.wait_online('veth-peer:carrier')
a962d857
YW
7981 additional_options = [
7982 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
7983 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
4001653d 7984 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
a962d857 7985 ]
625772c9 7986 if classless:
a962d857 7987 additional_options += [
86f67600 7988 '--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
7989 ]
7990 start_dnsmasq(*additional_options)
95e1fbba 7991 self.wait_online('veth99:routable', 'veth-peer:routable')
4c2e1833 7992
625772c9 7993 output = check_output('ip -4 route show dev veth99')
4c2e1833 7994 print(output)
4c2e1833 7995
7c0d36ff 7996 # Check UseRoutes=
625772c9
YW
7997 if use_routes:
7998 if classless:
7999 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8000 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 8001 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
8002 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8003 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8004 else:
4001653d 8005 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 8006 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 8007 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 8008 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 8009 else:
625772c9
YW
8010 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8011 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8012 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8013 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4001653d 8014 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 8015 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 8016 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 8017 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0d7bd445 8018
7c0d36ff 8019 # Check UseGateway=
625772c9
YW
8020 if use_gateway and (not classless or not use_routes):
8021 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 8022 else:
625772c9 8023 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32
YW
8024
8025 # Check route to gateway
8026 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
8027 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8028 else:
625772c9 8029 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7c0d36ff 8030
e1220a70
YW
8031 # Check RoutesToDNS= and RoutesToNTP=
8032 if dns_and_ntp_routes:
625772c9 8033 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 8034 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
0ce86f5e
YW
8035 if use_routes:
8036 if classless:
8037 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8038 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8039 else:
8040 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8041 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
87e62d32 8042 else:
625772c9 8043 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
e1220a70 8044 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
7c0d36ff 8045 else:
625772c9 8046 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
e1220a70 8047 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
625772c9 8048 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 8049 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 8050
10d670a3 8051 check_json(networkctl_json())
94f0bd62 8052
1f0e3109 8053 def test_dhcp_client_settings_anonymize(self):
a962d857 8054 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
2cf6fdff 8055 start_networkd()
95e1fbba 8056 self.wait_online('veth-peer:carrier')
ec38833c 8057 start_dnsmasq()
95e1fbba 8058 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 8059
063c7e9b
YW
8060 print('## dnsmasq log')
8061 output = read_dnsmasq_log_file()
8062 print(output)
8063 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
8064 self.assertNotIn('test-hostname', output)
8065 self.assertNotIn('26:mtu', output)
1f0e3109 8066
bbef21e4 8067 def test_dhcp_keep_configuration_dynamic(self):
a962d857
YW
8068 copy_network_unit('25-veth.netdev',
8069 '25-dhcp-server-veth-peer.network',
bbef21e4 8070 '25-dhcp-client-keep-configuration-dynamic.network')
2cf6fdff 8071 start_networkd()
95e1fbba 8072 self.wait_online('veth-peer:carrier')
a962d857 8073 start_dnsmasq()
95e1fbba 8074 self.wait_online('veth99:routable', 'veth-peer:routable')
e40a58b5 8075
1e498853
YW
8076 output = check_output('ip address show dev veth99 scope global')
8077 print(output)
2b6a24a6
YW
8078 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8079 'valid_lft forever preferred_lft forever')
e40a58b5 8080
5238e957 8081 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
888f57c1 8082 stop_dnsmasq()
1f0e3109
SS
8083
8084 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
a102a52c
YW
8085 print('Wait for the DHCP lease to be expired')
8086 time.sleep(120)
1f0e3109 8087
2b6a24a6 8088 # The lease address should be kept after the lease expired
1e498853
YW
8089 output = check_output('ip address show dev veth99 scope global')
8090 print(output)
2b6a24a6
YW
8091 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8092 'valid_lft forever preferred_lft forever')
1e498853 8093
2b6a24a6 8094 stop_networkd()
1e498853 8095
2b6a24a6 8096 # The lease address should be kept after networkd stopped
1e498853
YW
8097 output = check_output('ip address show dev veth99 scope global')
8098 print(output)
2b6a24a6
YW
8099 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8100 'valid_lft forever preferred_lft forever')
1e498853 8101
bbef21e4 8102 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f:
2347b6b9 8103 f.write('[Network]\nDHCP=no\n')
1e498853 8104
2347b6b9 8105 start_networkd()
95e1fbba 8106 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853 8107
2b6a24a6 8108 # Still the lease address should be kept after networkd restarted
1e498853
YW
8109 output = check_output('ip address show dev veth99 scope global')
8110 print(output)
2b6a24a6
YW
8111 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8112 'valid_lft forever preferred_lft forever')
1e498853 8113
bbef21e4 8114 def test_dhcp_keep_configuration_dynamic_on_stop(self):
a962d857
YW
8115 copy_network_unit('25-veth.netdev',
8116 '25-dhcp-server-veth-peer.network',
bbef21e4 8117 '25-dhcp-client-keep-configuration-dynamic-on-stop.network')
2cf6fdff 8118 start_networkd()
95e1fbba 8119 self.wait_online('veth-peer:carrier')
a962d857 8120 start_dnsmasq()
95e1fbba 8121 self.wait_online('veth99:routable', 'veth-peer:routable')
1e498853
YW
8122
8123 output = check_output('ip address show dev veth99 scope global')
8124 print(output)
2b6a24a6 8125 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 8126
888f57c1 8127 stop_dnsmasq()
2b6a24a6 8128 stop_networkd()
1e498853
YW
8129
8130 output = check_output('ip address show dev veth99 scope global')
8131 print(output)
2b6a24a6 8132 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
1e498853 8133
a962d857 8134 start_networkd()
95e1fbba 8135 self.wait_online('veth-peer:routable')
1e498853
YW
8136
8137 output = check_output('ip address show dev veth99 scope global')
8138 print(output)
2b6a24a6 8139 self.assertNotIn('192.168.5.', output)
1f0e3109 8140
30d3b54e 8141 def test_dhcp_client_reuse_address_as_static(self):
a962d857 8142 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
2cf6fdff 8143 start_networkd()
95e1fbba 8144 self.wait_online('veth-peer:carrier')
ec38833c 8145 start_dnsmasq()
95e1fbba 8146 self.wait_online('veth99:routable', 'veth-peer:routable')
2629df47
YW
8147
8148 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 8149 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 8150 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
30d3b54e 8151
371810d1 8152 output = check_output('ip address show dev veth99 scope global')
15519a81
YW
8153 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
8154 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
8155 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
30d3b54e
YW
8156 print(static_network)
8157
a962d857 8158 remove_network_unit('25-dhcp-client.network')
30d3b54e 8159
a962d857 8160 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
30d3b54e
YW
8161 f.write(static_network)
8162
15519a81 8163 restart_networkd()
95e1fbba 8164 self.wait_online('veth99:routable')
30d3b54e 8165
371810d1 8166 output = check_output('ip -4 address show dev veth99 scope global')
30d3b54e 8167 print(output)
15519a81
YW
8168 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
8169 'valid_lft forever preferred_lft forever')
30d3b54e 8170
371810d1 8171 output = check_output('ip -6 address show dev veth99 scope global')
30d3b54e 8172 print(output)
15519a81
YW
8173 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
8174 'valid_lft forever preferred_lft forever')
30d3b54e 8175
18c613dc
YW
8176 @expectedFailureIfModuleIsNotAvailable('vrf')
8177 def test_dhcp_client_vrf(self):
a962d857
YW
8178 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
8179 '25-vrf.netdev', '25-vrf.network')
2cf6fdff 8180 start_networkd()
95e1fbba 8181 self.wait_online('veth-peer:carrier')
ec38833c 8182 start_dnsmasq()
95e1fbba 8183 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
2629df47
YW
8184
8185 # link become 'routable' when at least one protocol provide an valid address.
3e726c15 8186 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 8187 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
18c613dc
YW
8188
8189 print('## ip -d link show dev vrf99')
371810d1 8190 output = check_output('ip -d link show dev vrf99')
18c613dc
YW
8191 print(output)
8192 self.assertRegex(output, 'vrf table 42')
8193
8194 print('## ip address show vrf vrf99')
371810d1 8195 output = check_output('ip address show vrf vrf99')
d90f4f7d 8196 print(output)
3e726c15 8197 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 8198 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
d90f4f7d 8199 self.assertRegex(output, 'inet6 .* scope link')
18c613dc
YW
8200
8201 print('## ip address show dev veth99')
371810d1 8202 output = check_output('ip address show dev veth99')
18c613dc 8203 print(output)
3e726c15 8204 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
426654d7 8205 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
18c613dc
YW
8206 self.assertRegex(output, 'inet6 .* scope link')
8207
8208 print('## ip route show vrf vrf99')
371810d1 8209 output = check_output('ip route show vrf vrf99')
18c613dc
YW
8210 print(output)
8211 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
18c613dc 8212 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
18c613dc
YW
8213 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
8214
8215 print('## ip route show table main dev veth99')
371810d1 8216 output = check_output('ip route show table main dev veth99')
18c613dc
YW
8217 print(output)
8218 self.assertEqual(output, '')
8219
af3b1498 8220 def test_dhcp_client_gateway_onlink_implicit(self):
a962d857
YW
8221 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8222 '25-dhcp-client-gateway-onlink-implicit.network')
2cf6fdff 8223 start_networkd()
95e1fbba 8224 self.wait_online('veth-peer:carrier')
ec38833c 8225 start_dnsmasq()
95e1fbba 8226 self.wait_online('veth99:routable', 'veth-peer:routable')
af3b1498 8227
10d670a3 8228 output = networkctl_status('veth99')
af3b1498
YW
8229 print(output)
8230 self.assertRegex(output, '192.168.5')
8231
371810d1 8232 output = check_output('ip route list dev veth99 10.0.0.0/8')
af3b1498
YW
8233 print(output)
8234 self.assertRegex(output, 'onlink')
371810d1 8235 output = check_output('ip route list dev veth99 192.168.100.0/24')
af3b1498
YW
8236 print(output)
8237 self.assertRegex(output, 'onlink')
8238
2d7a594f 8239 def test_dhcp_client_with_ipv4ll(self):
a962d857
YW
8240 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8241 '25-dhcp-client-with-ipv4ll.network')
2cf6fdff 8242 start_networkd()
2d7a594f
YW
8243 # we need to increase timeout above default, as this will need to wait for
8244 # systemd-networkd to get the dhcpv4 transient failure event
95e1fbba 8245 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
63c598ed 8246
2d7a594f 8247 output = check_output('ip -4 address show dev veth99')
63c598ed 8248 print(output)
2d7a594f 8249 self.assertNotIn('192.168.5.', output)
72c747e6 8250 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
63c598ed 8251
a962d857 8252 start_dnsmasq()
2d7a594f
YW
8253 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
8254 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
8255 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 8256 self.wait_online('veth99:routable')
63c598ed 8257
2d7a594f 8258 output = check_output('ip -4 address show dev veth99')
63c598ed 8259 print(output)
3e726c15 8260 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
2d7a594f
YW
8261 self.assertNotIn('169.254.', output)
8262 self.assertNotIn('scope link', output)
63c598ed 8263
2d7a594f
YW
8264 stop_dnsmasq()
8265 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
286bf3a9 8266 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 8267 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 8268
2d7a594f 8269 output = check_output('ip -4 address show dev veth99')
117a55c7 8270 print(output)
2d7a594f 8271 self.assertNotIn('192.168.5.', output)
72c747e6 8272 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
240e4137 8273
3d8e0aa2 8274 def test_dhcp_client_use_dns(self):
130d6695 8275 def check(self, ipv4, ipv6, needs_reconfigure=False):
a962d857
YW
8276 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8277 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
3d8e0aa2
YW
8278 f.write('[DHCPv4]\nUseDNS=')
8279 f.write('yes' if ipv4 else 'no')
8280 f.write('\n[DHCPv6]\nUseDNS=')
8281 f.write('yes' if ipv6 else 'no')
8282 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8283
a962d857 8284 networkctl_reload()
130d6695
YW
8285 if needs_reconfigure:
8286 networkctl_reconfigure('veth99')
95e1fbba 8287 self.wait_online('veth99:routable')
e2d5aab3 8288
3d8e0aa2
YW
8289 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8290 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8291 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
e2d5aab3 8292
3d8e0aa2 8293 # make resolved re-read the link state file
10d670a3 8294 resolvectl('revert', 'veth99')
e2d5aab3 8295
10d670a3 8296 output = resolvectl('dns', 'veth99')
3d8e0aa2
YW
8297 print(output)
8298 if ipv4:
8299 self.assertIn('192.168.5.1', output)
8300 else:
8301 self.assertNotIn('192.168.5.1', output)
8302 if ipv6:
8303 self.assertIn('2600::1', output)
8304 else:
8305 self.assertNotIn('2600::1', output)
e2d5aab3 8306
10d670a3 8307 check_json(networkctl_json())
e2d5aab3 8308
a962d857 8309 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
e2d5aab3
YW
8310
8311 start_networkd()
95e1fbba 8312 self.wait_online('veth-peer:carrier')
a962d857
YW
8313 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8314 '--dhcp-option=option6:dns-server,[2600::1]')
e2d5aab3 8315
3d8e0aa2
YW
8316 check(self, True, True)
8317 check(self, True, False)
130d6695 8318 check(self, False, True, needs_reconfigure=True)
3d8e0aa2 8319 check(self, False, False)
864c7980
YW
8320
8321 def test_dhcp_client_default_use_domains(self):
d51377ac 8322 def check(self, common, ipv4, ipv6):
fb573007
HL
8323 mkdir_p(networkd_conf_dropin_dir)
8324 with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
d51377ac
YW
8325 f.write('[Network]\nUseDomains=')
8326 f.write('yes\n' if common else 'no\n')
fb573007
HL
8327 f.write('[DHCPv4]\nUseDomains=')
8328 f.write('yes\n' if ipv4 else 'no\n')
8329 f.write('[DHCPv6]\nUseDomains=')
8330 f.write('yes\n' if ipv6 else 'no\n')
864c7980 8331
fb573007
HL
8332 restart_networkd()
8333 self.wait_online('veth-peer:carrier')
8334 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8335 '--dhcp-option=option6:dns-server,[2600::1]',
8336 '--dhcp-option=option:domain-search,example.com',
8337 '--dhcp-option=option6:domain-search,example.com')
8338
8339 self.wait_online('veth99:routable')
8340
8341 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8342 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8343 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8344
8345 for _ in range(20):
8346 output = resolvectl('domain', 'veth99')
d51377ac 8347 if common or ipv4 or ipv6:
fb573007
HL
8348 if 'example.com' in output:
8349 break
8350 else:
8351 if 'example.com' not in output:
8352 break
8353 time.sleep(0.5)
8354 else:
8355 print(output)
d51377ac 8356 print(read_link_state_file('veth99'))
fb573007 8357 self.fail('unexpected domain setting in resolved...')
864c7980 8358
fb573007
HL
8359 stop_dnsmasq()
8360 remove_networkd_conf_dropin('default_use_domains.conf')
8361
8362 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
d51377ac
YW
8363 check(self, True, False, False)
8364 check(self, False, True, True)
8365 check(self, False, True, False)
8366 check(self, False, False, True)
8367 check(self, False, False, False)
e2d5aab3 8368
7957154e 8369 def test_dhcp_client_use_dnr(self):
130d6695 8370 def check(self, ipv4, ipv6, needs_reconfigure=False):
7957154e
RP
8371 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8372 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8373 f.write('[DHCPv4]\nUseDNS=')
8374 f.write('yes' if ipv4 else 'no')
8375 f.write('\n[DHCPv6]\nUseDNS=')
8376 f.write('yes' if ipv6 else 'no')
8377 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8378
8379 networkctl_reload()
130d6695
YW
8380 if needs_reconfigure:
8381 networkctl_reconfigure('veth99')
7957154e
RP
8382 self.wait_online('veth99:routable')
8383
8384 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8385 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8386 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8387
8388 # make resolved re-read the link state file
8389 resolvectl('revert', 'veth99')
8390
8391 output = resolvectl('dns', 'veth99')
8392 print(output)
8393 if ipv4:
8394 self.assertIn('8.8.8.8#dns.google', output)
8395 self.assertIn('0.7.4.2#homer.simpson', output)
8396 else:
8397 self.assertNotIn('8.8.8.8#dns.google', output)
cb386795
RP
8398 if ipv6:
8399 self.assertIn('2001:4860:4860::8888#dns.google', output)
8400 else:
8401 self.assertNotIn('2001:4860:4860::8888#dns.google', output)
7957154e
RP
8402
8403 check_json(networkctl_json())
8404
8405 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8406
8407 start_networkd()
8408 self.wait_online('veth-peer:carrier')
8409 dnr_v4 = dnr_v4_instance_data(adn = "dns.google", addrs = ["8.8.8.8", "8.8.4.4"])
8410 dnr_v4 += dnr_v4_instance_data(adn = "homer.simpson", addrs = ["0.7.4.2"], alpns = ("dot","h2","h3"), dohpath = "/springfield{?dns}")
cb386795 8411 dnr_v6 = dnr_v6_instance_data(adn = "dns.google", addrs = ["2001:4860:4860::8888", "2001:4860:4860::8844"])
7957154e 8412 masq = lambda bs: ':'.join(f"{b:02x}" for b in bs)
cb386795
RP
8413 start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}',
8414 f'--dhcp-option=option6:144,{masq(dnr_v6)}')
7957154e 8415
cb386795 8416 check(self, True, True)
7957154e 8417 check(self, True, False)
130d6695 8418 check(self, False, True, needs_reconfigure=True)
7957154e
RP
8419 check(self, False, False)
8420
dbe960f0 8421 def test_dhcp_client_use_captive_portal(self):
130d6695 8422 def check(self, ipv4, ipv6, needs_reconfigure=False):
dbe960f0
RP
8423 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8424 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8425 f.write('[DHCPv4]\nUseCaptivePortal=')
8426 f.write('yes' if ipv4 else 'no')
8427 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8428 f.write('yes' if ipv6 else 'no')
8429 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8430
8431 networkctl_reload()
130d6695
YW
8432 if needs_reconfigure:
8433 networkctl_reconfigure('veth99')
95e1fbba 8434 self.wait_online('veth99:routable')
dbe960f0
RP
8435
8436 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8437 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8438 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8439
10d670a3 8440 output = networkctl_status('veth99')
dbe960f0
RP
8441 print(output)
8442 if ipv4 or ipv6:
8443 self.assertIn('Captive Portal: http://systemd.io', output)
8444 else:
8445 self.assertNotIn('Captive Portal: http://systemd.io', output)
8446
10d670a3 8447 check_json(networkctl_json())
dbe960f0
RP
8448
8449 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8450
8451 start_networkd()
95e1fbba 8452 self.wait_online('veth-peer:carrier')
dbe960f0
RP
8453 start_dnsmasq('--dhcp-option=114,http://systemd.io',
8454 '--dhcp-option=option6:103,http://systemd.io')
8455
8456 check(self, True, True)
8457 check(self, True, False)
130d6695 8458 check(self, False, True, needs_reconfigure=True)
dbe960f0
RP
8459 check(self, False, False)
8460
1219391c
RP
8461 def test_dhcp_client_reject_captive_portal(self):
8462 def check(self, ipv4, ipv6):
8463 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8464 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8465 f.write('[DHCPv4]\nUseCaptivePortal=')
8466 f.write('yes' if ipv4 else 'no')
8467 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8468 f.write('yes' if ipv6 else 'no')
8469 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8470
8471 networkctl_reload()
95e1fbba 8472 self.wait_online('veth99:routable')
1219391c
RP
8473
8474 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8475 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8476 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8477
10d670a3 8478 output = networkctl_status('veth99')
1219391c
RP
8479 print(output)
8480 self.assertNotIn('Captive Portal: ', output)
8481 self.assertNotIn('invalid/url', output)
8482
10d670a3 8483 check_json(networkctl_json())
1219391c
RP
8484
8485 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8486
8487 start_networkd()
95e1fbba 8488 self.wait_online('veth-peer:carrier')
1219391c
RP
8489 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
8490 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
8491 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
8492
8493 check(self, True, True)
8494 check(self, True, False)
8495 check(self, False, True)
8496 check(self, False, False)
8497
a27588d4 8498class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
caad88a2
YW
8499
8500 def setUp(self):
a962d857 8501 setup_common()
caad88a2
YW
8502
8503 def tearDown(self):
a962d857 8504 tear_down_common()
caad88a2 8505
ab06b74f
YW
8506 def check_dhcp6_prefix(self, link):
8507 description = get_link_description(link)
8fb6320e 8508
ab06b74f
YW
8509 self.assertIn('DHCPv6Client', description.keys())
8510 self.assertIn('Prefixes', description['DHCPv6Client'])
8fb6320e 8511
ab06b74f 8512 prefixInfo = description['DHCPv6Client']['Prefixes']
8fb6320e 8513
ab06b74f 8514 self.assertEqual(len(prefixInfo), 1)
8fb6320e 8515
ab06b74f
YW
8516 self.assertIn('Prefix', prefixInfo[0].keys())
8517 self.assertIn('PrefixLength', prefixInfo[0].keys())
8518 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
8519 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
caad88a2 8520
ab06b74f
YW
8521 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
8522 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
8523 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
8524 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
8525
1761c352 8526 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
ab06b74f
YW
8527 def test_dhcp6pd_no_address(self):
8528 # For issue #29979.
8529 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
c742d7e8 8530
caad88a2 8531 start_networkd()
95e1fbba 8532 self.wait_online('veth-peer:routable')
a962d857 8533 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba 8534 self.wait_online('veth99:degraded')
1805e2cb 8535
1805e2cb
YW
8536 print('### ip -6 address show dev veth99 scope global')
8537 output = check_output('ip -6 address show dev veth99 scope global')
8538 print(output)
8539 self.assertNotIn('inet6 3ffe:501:ffff', output)
caad88a2 8540
d1335084
YW
8541 print('### ip -6 route show dev lo')
8542 output = check_output('ip -6 route show dev lo')
8543 print(output)
8544 self.assertNotRegex(output, '3ffe:501:ffff:[2-9a-f]00::/56')
8545
ab06b74f 8546 self.check_dhcp6_prefix('veth99')
8fb6320e 8547
1761c352 8548 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
3b677c6f
YW
8549 def test_dhcp6pd_no_assign(self):
8550 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
8551 # However, the server does not provide IA_NA. For issue #31349.
8552 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
8553
8554 start_networkd()
8555 self.wait_online('veth-peer:routable')
8556 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
8557 self.wait_online('veth99:degraded')
8558
8559 print('### ip -6 address show dev veth99 scope global')
8560 output = check_output('ip -6 address show dev veth99 scope global')
8561 print(output)
8562 self.assertNotIn('inet6 3ffe:501:ffff', output)
8563
d1335084
YW
8564 print('### ip -6 route show type blackhole')
8565 output = check_output('ip -6 route show type blackhole')
8566 print(output)
8567 self.assertRegex(output, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8568
3b677c6f
YW
8569 self.check_dhcp6_prefix('veth99')
8570
1761c352 8571 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
ab06b74f
YW
8572 def test_dhcp6pd(self):
8573 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
8574 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8575 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8576 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8577 '25-dhcp-pd-downstream-dummy97.network',
8578 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8579 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
8fb6320e 8580
ab06b74f
YW
8581 start_networkd()
8582 self.wait_online('veth-peer:routable')
8583 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
95e1fbba
YW
8584 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8585 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
1805e2cb 8586
ab06b74f
YW
8587 self.setup_nftset('addr6', 'ipv6_addr')
8588 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
8589 self.setup_nftset('ifindex', 'iface_index')
8590
8591 # Check DBus assigned prefix information to veth99
8592 self.check_dhcp6_prefix('veth99')
8593
caad88a2
YW
8594 print('### ip -6 address show dev veth-peer scope global')
8595 output = check_output('ip -6 address show dev veth-peer scope global')
8596 print(output)
8597 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
8598
f7805a6c
FS
8599 # Link Subnet IDs
8600 # test1: 0x00
8601 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
8602 # dummy98: 0x00
8603 # dummy99: auto -> 0x02 (No address assignment)
f7805a6c
FS
8604 # veth97: 0x08
8605 # veth98: 0x09
38488bab 8606 # veth99: 0x10
6016f1cf 8607
caad88a2
YW
8608 print('### ip -6 address show dev veth99 scope global')
8609 output = check_output('ip -6 address show dev veth99 scope global')
8610 print(output)
8611 # IA_NA
8612 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8613 # address in IA_PD (Token=static)
38488bab 8614 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
caad88a2 8615 # address in IA_PD (Token=eui64)
38488bab
YW
8616 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
8617 # address in IA_PD (temporary)
8618 # Note that the temporary addresses may appear after the link enters configured state
8619 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
8620
8621 print('### ip -6 address show dev test1 scope global')
8622 output = check_output('ip -6 address show dev test1 scope global')
8623 print(output)
8624 # address in IA_PD (Token=static)
8625 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8626 # address in IA_PD (temporary)
8627 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')
8628
8629 print('### ip -6 address show dev dummy98 scope global')
8630 output = check_output('ip -6 address show dev dummy98 scope global')
8631 print(output)
8632 # address in IA_PD (Token=static)
07b7337a 8633 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 8634 # address in IA_PD (temporary)
07b7337a 8635 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
8636
8637 print('### ip -6 address show dev dummy99 scope global')
8638 output = check_output('ip -6 address show dev dummy99 scope global')
8639 print(output)
8640 # Assign=no
07b7337a 8641 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2 8642
6c8d6bdd
YW
8643 print('### ip -6 address show dev veth97 scope global')
8644 output = check_output('ip -6 address show dev veth97 scope global')
8645 print(output)
8646 # address in IA_PD (Token=static)
8647 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8648 # address in IA_PD (Token=eui64)
8649 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8650 # address in IA_PD (temporary)
8651 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')
8652
8653 print('### ip -6 address show dev veth97-peer scope global')
8654 output = check_output('ip -6 address show dev veth97-peer scope global')
8655 print(output)
8656 # NDisc address (Token=static)
8657 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8658 # NDisc address (Token=eui64)
8659 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8660 # NDisc address (temporary)
8661 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')
8662
caad88a2
YW
8663 print('### ip -6 address show dev veth98 scope global')
8664 output = check_output('ip -6 address show dev veth98 scope global')
8665 print(output)
8666 # address in IA_PD (Token=static)
8667 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8668 # address in IA_PD (Token=eui64)
8669 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8670 # address in IA_PD (temporary)
8671 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')
8672
8673 print('### ip -6 address show dev veth98-peer scope global')
8674 output = check_output('ip -6 address show dev veth98-peer scope global')
8675 print(output)
8676 # NDisc address (Token=static)
8677 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8678 # NDisc address (Token=eui64)
8679 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8680 # NDisc address (temporary)
8681 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')
8682
caad88a2
YW
8683 print('### ip -6 route show type unreachable')
8684 output = check_output('ip -6 route show type unreachable')
8685 print(output)
8686 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8687
8688 print('### ip -6 route show dev veth99')
8689 output = check_output('ip -6 route show dev veth99')
8690 print(output)
38488bab 8691 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
8692
8693 print('### ip -6 route show dev test1')
8694 output = check_output('ip -6 route show dev test1')
8695 print(output)
8696 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8697
8698 print('### ip -6 route show dev dummy98')
8699 output = check_output('ip -6 route show dev dummy98')
8700 print(output)
07b7337a 8701 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
8702
8703 print('### ip -6 route show dev dummy99')
8704 output = check_output('ip -6 route show dev dummy99')
8705 print(output)
07b7337a 8706 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6016f1cf
YW
8707
8708 print('### ip -6 route show dev veth97')
8709 output = check_output('ip -6 route show dev veth97')
8710 print(output)
8711 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
8712
8713 print('### ip -6 route show dev veth97-peer')
8714 output = check_output('ip -6 route show dev veth97-peer')
8715 print(output)
8716 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
caad88a2
YW
8717
8718 print('### ip -6 route show dev veth98')
8719 output = check_output('ip -6 route show dev veth98')
8720 print(output)
8721 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
8722
8723 print('### ip -6 route show dev veth98-peer')
8724 output = check_output('ip -6 route show dev veth98-peer')
8725 print(output)
8726 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
8727
8728 # Test case for a downstream which appears later
8729 check_output('ip link add dummy97 type dummy')
95e1fbba 8730 self.wait_online('dummy97:routable')
caad88a2
YW
8731
8732 print('### ip -6 address show dev dummy97 scope global')
8733 output = check_output('ip -6 address show dev dummy97 scope global')
8734 print(output)
8735 # address in IA_PD (Token=static)
6016f1cf 8736 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 8737 # address in IA_PD (temporary)
6016f1cf 8738 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
8739
8740 print('### ip -6 route show dev dummy97')
8741 output = check_output('ip -6 route show dev dummy97')
8742 print(output)
6016f1cf 8743 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
caad88a2
YW
8744
8745 # Test case for reconfigure
a962d857 8746 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 8747 self.wait_online('dummy98:routable', 'dummy99:degraded')
caad88a2
YW
8748
8749 print('### ip -6 address show dev dummy98 scope global')
8750 output = check_output('ip -6 address show dev dummy98 scope global')
8751 print(output)
8752 # address in IA_PD (Token=static)
07b7337a 8753 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
caad88a2 8754 # address in IA_PD (temporary)
07b7337a 8755 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
8756
8757 print('### ip -6 address show dev dummy99 scope global')
8758 output = check_output('ip -6 address show dev dummy99 scope global')
8759 print(output)
8760 # Assign=no
07b7337a 8761 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
caad88a2
YW
8762
8763 print('### ip -6 route show dev dummy98')
8764 output = check_output('ip -6 route show dev dummy98')
8765 print(output)
07b7337a 8766 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6016f1cf
YW
8767
8768 print('### ip -6 route show dev dummy99')
8769 output = check_output('ip -6 route show dev dummy99')
8770 print(output)
07b7337a 8771 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
caad88a2 8772
a4640bed
TM
8773 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
8774
c742d7e8
TM
8775 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
8776 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
8777 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
8778 self.check_nftset('ifindex', 'dummy98')
8779
8780 self.teardown_nftset('addr6', 'network6', 'ifindex')
8781
59528e55 8782 def verify_dhcp4_6rd(self, tunnel_name, address_prefix, border_router):
84cc85f9
YW
8783 print('### ip -4 address show dev veth-peer scope global')
8784 output = check_output('ip -4 address show dev veth-peer scope global')
8785 print(output)
8786 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
8787
f7805a6c
FS
8788 # Link Subnet IDs
8789 # test1: 0x00
8790 # dummy97: 0x01 (The link will appear later)
07b7337a
YW
8791 # dummy98: 0x00
8792 # dummy99: auto -> 0x0[23] (No address assignment)
8793 # 6rd-XXX: auto -> 0x0[23]
f7805a6c
FS
8794 # veth97: 0x08
8795 # veth98: 0x09
8796 # veth99: 0x10
84cc85f9
YW
8797
8798 print('### ip -4 address show dev veth99 scope global')
8799 output = check_output('ip -4 address show dev veth99 scope global')
8800 print(output)
59528e55 8801 self.assertRegex(output, fr'inet {address_prefix}[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
84cc85f9
YW
8802
8803 print('### ip -6 address show dev veth99 scope global')
8804 output = check_output('ip -6 address show dev veth99 scope global')
8805 print(output)
8806 # address in IA_PD (Token=static)
8807 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8808 # address in IA_PD (Token=eui64)
8809 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
8810 # address in IA_PD (temporary)
8811 # Note that the temporary addresses may appear after the link enters configured state
8812 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')
8813
8814 print('### ip -6 address show dev test1 scope global')
8815 output = check_output('ip -6 address show dev test1 scope global')
8816 print(output)
8817 # address in IA_PD (Token=static)
8818 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8819 # address in IA_PD (temporary)
8820 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')
8821
8822 print('### ip -6 address show dev dummy98 scope global')
8823 output = check_output('ip -6 address show dev dummy98 scope global')
8824 print(output)
8825 # address in IA_PD (Token=static)
07b7337a 8826 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
84cc85f9 8827 # address in IA_PD (temporary)
07b7337a 8828 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
8829
8830 print('### ip -6 address show dev dummy99 scope global')
8831 output = check_output('ip -6 address show dev dummy99 scope global')
8832 print(output)
8833 # Assign=no
07b7337a 8834 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
84cc85f9
YW
8835
8836 print('### ip -6 address show dev veth97 scope global')
8837 output = check_output('ip -6 address show dev veth97 scope global')
8838 print(output)
8839 # address in IA_PD (Token=static)
8840 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8841 # address in IA_PD (Token=eui64)
8842 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8843 # address in IA_PD (temporary)
8844 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')
8845
8846 print('### ip -6 address show dev veth97-peer scope global')
8847 output = check_output('ip -6 address show dev veth97-peer scope global')
8848 print(output)
8849 # NDisc address (Token=static)
8850 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8851 # NDisc address (Token=eui64)
8852 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8853 # NDisc address (temporary)
8854 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')
8855
8856 print('### ip -6 address show dev veth98 scope global')
8857 output = check_output('ip -6 address show dev veth98 scope global')
8858 print(output)
8859 # address in IA_PD (Token=static)
8860 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8861 # address in IA_PD (Token=eui64)
8862 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8863 # address in IA_PD (temporary)
8864 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')
8865
8866 print('### ip -6 address show dev veth98-peer scope global')
8867 output = check_output('ip -6 address show dev veth98-peer scope global')
8868 print(output)
8869 # NDisc address (Token=static)
8870 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8871 # NDisc address (Token=eui64)
8872 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8873 # NDisc address (temporary)
8874 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')
8875
8876 print('### ip -6 route show type unreachable')
8877 output = check_output('ip -6 route show type unreachable')
8878 print(output)
8879 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
8880
8881 print('### ip -6 route show dev veth99')
8882 output = check_output('ip -6 route show dev veth99')
8883 print(output)
8884 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
8885
8886 print('### ip -6 route show dev test1')
8887 output = check_output('ip -6 route show dev test1')
8888 print(output)
8889 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
8890
8891 print('### ip -6 route show dev dummy98')
8892 output = check_output('ip -6 route show dev dummy98')
8893 print(output)
07b7337a 8894 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
8895
8896 print('### ip -6 route show dev dummy99')
8897 output = check_output('ip -6 route show dev dummy99')
8898 print(output)
07b7337a 8899 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
84cc85f9
YW
8900
8901 print('### ip -6 route show dev veth97')
8902 output = check_output('ip -6 route show dev veth97')
8903 print(output)
8904 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
8905
8906 print('### ip -6 route show dev veth97-peer')
8907 output = check_output('ip -6 route show dev veth97-peer')
8908 print(output)
8909 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
8910
8911 print('### ip -6 route show dev veth98')
8912 output = check_output('ip -6 route show dev veth98')
8913 print(output)
8914 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
8915
8916 print('### ip -6 route show dev veth98-peer')
8917 output = check_output('ip -6 route show dev veth98-peer')
8918 print(output)
8919 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
8920
84cc85f9
YW
8921 print('### ip -6 address show dev dummy97 scope global')
8922 output = check_output('ip -6 address show dev dummy97 scope global')
8923 print(output)
8924 # address in IA_PD (Token=static)
8925 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8926 # address in IA_PD (temporary)
8927 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')
8928
8929 print('### ip -6 route show dev dummy97')
8930 output = check_output('ip -6 route show dev dummy97')
8931 print(output)
8932 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
8933
e4295d4d
FS
8934 print(f'### ip -d link show dev {tunnel_name}')
8935 output = check_output(f'ip -d link show dev {tunnel_name}')
84cc85f9 8936 print(output)
59528e55
YW
8937 self.assertIn(f'link/sit {address_prefix}', output)
8938 self.assertIn(f'local {address_prefix}', output)
84cc85f9
YW
8939 self.assertIn('ttl 64', output)
8940 self.assertIn('6rd-prefix 2001:db8::/32', output)
8941 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
8942
e4295d4d
FS
8943 print(f'### ip -6 address show dev {tunnel_name}')
8944 output = check_output(f'ip -6 address show dev {tunnel_name}')
84cc85f9 8945 print(output)
07b7337a 8946 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')
59528e55 8947 self.assertRegex(output, fr'inet6 ::{address_prefix}[0-9]+/96 scope global')
84cc85f9 8948
e4295d4d
FS
8949 print(f'### ip -6 route show dev {tunnel_name}')
8950 output = check_output(f'ip -6 route show dev {tunnel_name}')
84cc85f9 8951 print(output)
07b7337a 8952 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
84cc85f9
YW
8953 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
8954
8955 print('### ip -6 route show default')
8956 output = check_output('ip -6 route show default')
8957 print(output)
8958 self.assertIn('default', output)
59528e55 8959 self.assertIn(f'via ::{border_router} dev {tunnel_name}', output)
84cc85f9 8960
6a936c9c 8961 def test_dhcp4_6rd(self):
e081ffc1
YW
8962 def get_dhcp_6rd_prefix(link):
8963 description = get_link_description(link)
8fb6320e 8964
8965 self.assertIn('DHCPv4Client', description.keys())
8966 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
8967
8968 prefixInfo = description['DHCPv4Client']['6rdPrefix']
8969 self.assertIn('Prefix', prefixInfo.keys())
8970 self.assertIn('PrefixLength', prefixInfo.keys())
8971 self.assertIn('IPv4MaskLength', prefixInfo.keys())
8972 self.assertIn('BorderRouters', prefixInfo.keys())
8973
8974 return prefixInfo
8975
a962d857
YW
8976 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
8977 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8978 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8979 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8980 '25-dhcp-pd-downstream-dummy97.network',
8981 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8982 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
8983 '80-6rd-tunnel.network')
6a936c9c
YW
8984
8985 start_networkd()
95e1fbba 8986 self.wait_online('veth-peer:routable')
f7805a6c
FS
8987
8988 # ipv4masklen: 8
8989 # 6rd-prefix: 2001:db8::/32
8990 # br-addresss: 10.0.0.1
8991
a962d857
YW
8992 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',
8993 ipv4_range='10.100.100.100,10.100.100.200',
8994 ipv4_router='10.0.0.1')
95e1fbba
YW
8995 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8996 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c 8997
8fb6320e 8998 # Check the DBus interface for assigned prefix information
e081ffc1 8999 prefixInfo = get_dhcp_6rd_prefix('veth99')
8fb6320e 9000
9001 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
9002 self.assertEqual(prefixInfo['PrefixLength'], 32)
9003 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
9004 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
9005
6a936c9c
YW
9006 # Test case for a downstream which appears later
9007 check_output('ip link add dummy97 type dummy')
95e1fbba 9008 self.wait_online('dummy97:routable')
6a936c9c
YW
9009
9010 # Find tunnel name
9011 tunnel_name = None
9012 for name in os.listdir('/sys/class/net/'):
9013 if name.startswith('6rd-'):
9014 tunnel_name = name
9015 break
9016
95e1fbba 9017 self.wait_online(f'{tunnel_name}:routable')
6a936c9c 9018
59528e55 9019 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
6a936c9c
YW
9020
9021 # Test case for reconfigure
a962d857 9022 networkctl_reconfigure('dummy98', 'dummy99')
95e1fbba 9023 self.wait_online('dummy98:routable', 'dummy99:degraded')
6a936c9c 9024
59528e55
YW
9025 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
9026
9027 # Change the address range and (border) router, then if check the same tunnel is reused.
9028 stop_dnsmasq()
9029 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:02',
9030 ipv4_range='10.100.100.200,10.100.100.250',
9031 ipv4_router='10.0.0.2')
6a936c9c 9032
3af934bc 9033 print('Wait for the DHCP lease to be renewed/rebind')
a102a52c 9034 time.sleep(120)
6a936c9c 9035
95e1fbba
YW
9036 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
9037 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6a936c9c 9038
59528e55 9039 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.2', '10.0.0.2')
6a936c9c 9040
9633f977 9041class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
9633f977
SS
9042
9043 def setUp(self):
a962d857 9044 setup_common()
9633f977
SS
9045
9046 def tearDown(self):
a962d857 9047 tear_down_common()
9633f977
SS
9048
9049 def test_ipv6_route_prefix(self):
a962d857
YW
9050 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
9051 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9633f977
SS
9052
9053 start_networkd()
95e1fbba 9054 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9633f977 9055
d7b323c2
YW
9056 print('### ip -6 address show dev veth-peer')
9057 output = check_output('ip -6 address show dev veth-peer')
635f2a66
YW
9058 print(output)
9059 self.assertIn('inet6 2001:db8:0:1:', output)
9060 self.assertNotIn('inet6 2001:db8:0:2:', output)
ab47f960 9061 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 9062
d7b323c2 9063 print('### ip -6 route show dev veth-peer')
3c874fd7 9064 output = check_output('ip -6 route show dev veth-peer')
9633f977 9065 print(output)
635f2a66
YW
9066 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9067 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
ab47f960 9068 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
0f8afaf9 9069 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
d7b323c2
YW
9070 self.assertNotIn('2001:db1:fff::/64', output)
9071 self.assertNotIn('2001:db2:fff::/64', output)
9633f977 9072
0f8afaf9
YW
9073 print('### ip -6 nexthop show dev veth-peer')
9074 output = check_output('ip -6 nexthop show dev veth-peer')
9075 print(output)
9076 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9077
d7b323c2
YW
9078 print('### ip -6 address show dev veth99')
9079 output = check_output('ip -6 address show dev veth99')
635f2a66
YW
9080 print(output)
9081 self.assertNotIn('inet6 2001:db8:0:1:', output)
fe2a8b3d
YW
9082 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
9083 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
ab47f960 9084 self.assertNotIn('inet6 2001:db8:0:3:', output)
635f2a66 9085
10d670a3 9086 output = resolvectl('dns', 'veth-peer')
4a906586
YW
9087 print(output)
9088 self.assertRegex(output, '2001:db8:1:1::2')
9089
10d670a3 9090 output = resolvectl('domain', 'veth-peer')
4a906586
YW
9091 print(output)
9092 self.assertIn('example.com', output)
9093
10d670a3 9094 check_json(networkctl_json())
94f0bd62 9095
10d670a3 9096 output = networkctl_json('veth-peer')
681007ac
SS
9097 check_json(output)
9098
9099 # PREF64 or NAT64
9100 pref64 = json.loads(output)['NDisc']['PREF64'][0]
9101
9102 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
9103 self.assertEqual(prefix, '64:ff9b::')
9104
9105 prefix_length = pref64['PrefixLength']
9106 self.assertEqual(prefix_length, 96)
9107
635f2a66 9108 def test_ipv6_route_prefix_deny_list(self):
a962d857
YW
9109 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
9110 '12-dummy.netdev', '25-ipv6ra-uplink.network')
635f2a66
YW
9111
9112 start_networkd()
95e1fbba 9113 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
635f2a66 9114
d7b323c2
YW
9115 print('### ip -6 address show dev veth-peer')
9116 output = check_output('ip -6 address show dev veth-peer')
635f2a66
YW
9117 print(output)
9118 self.assertIn('inet6 2001:db8:0:1:', output)
9119 self.assertNotIn('inet6 2001:db8:0:2:', output)
9120
d7b323c2 9121 print('### ip -6 route show dev veth-peer')
635f2a66
YW
9122 output = check_output('ip -6 route show dev veth-peer')
9123 print(output)
9124 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9125 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
0f8afaf9 9126 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
d7b323c2 9127 self.assertNotIn('2001:db1:fff::/64', output)
635f2a66 9128
0f8afaf9
YW
9129 print('### ip -6 nexthop show dev veth-peer')
9130 output = check_output('ip -6 nexthop show dev veth-peer')
9131 print(output)
9132 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9133
d7b323c2
YW
9134 print('### ip -6 address show dev veth99')
9135 output = check_output('ip -6 address show dev veth99')
3c874fd7 9136 print(output)
635f2a66
YW
9137 self.assertNotIn('inet6 2001:db8:0:1:', output)
9138 self.assertIn('inet6 2001:db8:0:2:', output)
3c874fd7 9139
10d670a3 9140 output = resolvectl('dns', 'veth-peer')
4a906586
YW
9141 print(output)
9142 self.assertRegex(output, '2001:db8:1:1::2')
9143
10d670a3 9144 output = resolvectl('domain', 'veth-peer')
4a906586
YW
9145 print(output)
9146 self.assertIn('example.com', output)
9147
7db05447 9148class NetworkdMTUTests(unittest.TestCase, Utilities):
7db05447
DS
9149
9150 def setUp(self):
a962d857 9151 setup_common()
7db05447
DS
9152
9153 def tearDown(self):
a962d857 9154 tear_down_common()
7db05447
DS
9155
9156 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
9157 if not ipv6_mtu:
9158 ipv6_mtu = mtu
9159
9160 # test normal start
9161 start_networkd()
95e1fbba 9162 self.wait_online('dummy98:routable')
a962d857
YW
9163 self.check_link_attr('dummy98', 'mtu', mtu)
9164 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
9165
9166 # test normal restart
9167 restart_networkd()
95e1fbba 9168 self.wait_online('dummy98:routable')
a962d857
YW
9169 self.check_link_attr('dummy98', 'mtu', mtu)
9170 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7db05447
DS
9171
9172 if reset:
9173 self.reset_check_mtu(mtu, ipv6_mtu)
9174
9175 def reset_check_mtu(self, mtu, ipv6_mtu=None):
9176 ''' test setting mtu/ipv6_mtu with interface already up '''
9177 stop_networkd()
9178
9179 # note - changing the device mtu resets the ipv6 mtu
a962d857
YW
9180 check_output('ip link set up mtu 1501 dev dummy98')
9181 check_output('ip link set up mtu 1500 dev dummy98')
9182 self.check_link_attr('dummy98', 'mtu', '1500')
9183 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7db05447
DS
9184
9185 self.check_mtu(mtu, ipv6_mtu, reset=False)
9186
9187 def test_mtu_network(self):
a962d857 9188 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7db05447
DS
9189 self.check_mtu('1600')
9190
9191 def test_mtu_netdev(self):
a962d857 9192 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
7db05447
DS
9193 # note - MTU set by .netdev happens ONLY at device creation!
9194 self.check_mtu('1600', reset=False)
9195
9196 def test_mtu_link(self):
a962d857 9197 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
7db05447
DS
9198 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
9199 self.check_mtu('1600', reset=False)
9200
9201 def test_ipv6_mtu(self):
9202 ''' set ipv6 mtu without setting device mtu '''
a962d857 9203 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7db05447
DS
9204 self.check_mtu('1500', '1400')
9205
9206 def test_ipv6_mtu_toolarge(self):
9207 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
a962d857 9208 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
9209 self.check_mtu('1500', '1500')
9210
9211 def test_mtu_network_ipv6_mtu(self):
9212 ''' set ipv6 mtu and set device mtu via network file '''
a962d857 9213 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
9214 self.check_mtu('1600', '1550')
9215
9216 def test_mtu_netdev_ipv6_mtu(self):
9217 ''' set ipv6 mtu and set device mtu via netdev file '''
a962d857 9218 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
9219 self.check_mtu('1600', '1550', reset=False)
9220
9221 def test_mtu_link_ipv6_mtu(self):
9222 ''' set ipv6 mtu and set device mtu via link file '''
a962d857 9223 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7db05447
DS
9224 self.check_mtu('1600', '1550', reset=False)
9225
c78bcda4
MC
9226class NetworkdSysctlTest(unittest.TestCase, Utilities):
9227
9228 def setUp(self):
9229 setup_common()
9230
9231 def tearDown(self):
9232 tear_down_common()
9233
9234 @unittest.skipUnless(compare_kernel_version("6.12"), reason="On kernels <= 6.12, bpf_current_task_under_cgroup() isn't available for program types BPF_PROG_TYPE_CGROUP_SYSCTL")
9235 def check_sysctl_watch(self):
9236 copy_network_unit('12-dummy.network', '12-dummy.netdev', '12-dummy.link')
9237 start_networkd()
9238
9239 self.wait_online('dummy98:routable')
9240
9241 # Change managed sysctls
9242 call('sysctl -w net.ipv6.conf.dummy98.accept_ra=1')
9243 call('sysctl -w net.ipv6.conf.dummy98.mtu=1360')
9244 call('sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0')
9245 call('sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1')
9246
9247 # And unmanaged ones
9248 call('sysctl -w net.ipv6.conf.dummy98.hop_limit=4')
9249 call('sysctl -w net.ipv6.conf.dummy98.max_addresses=10')
9250
9251 log=read_networkd_log()
9252 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/accept_ra' from '0' to '1', conflicting with our setting to '0'")
9253 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/mtu' from '1550' to '1360', conflicting with our setting to '1550'")
9254 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv4/conf/dummy98/promote_secondaries' from '1' to '0', conflicting with our setting to '1'")
9255 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/proxy_ndp' from '0' to '1', conflicting with our setting to '0'")
9256 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'", log)
9257 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log)
7db05447 9258
1f0e3109 9259if __name__ == '__main__':
9c1ae484
YW
9260 parser = argparse.ArgumentParser()
9261 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
33b0e0c0 9262 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
9c1ae484
YW
9263 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
9264 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
94c03122 9265 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
fa4c6095 9266 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
94c03122 9267 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
6c9efba6 9268 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)
e92d7b7d 9269 parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false')
a561bcee 9270 ns, unknown_args = parser.parse_known_args(namespace=unittest)
9c1ae484
YW
9271
9272 if ns.build_dir:
9c1ae484 9273 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
b6d587d1 9274 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
b05c4d6b 9275 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
9c1ae484
YW
9276 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
9277 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
b6d587d1
YW
9278 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
9279 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
b05c4d6b 9280 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
f66045c7 9281 build_dir = ns.build_dir
9c1ae484 9282
33b0e0c0 9283 if ns.source_dir:
f66045c7 9284 source_dir = ns.source_dir
f2adc1de
DDM
9285 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
9286 elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))):
f66045c7 9287 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
f2adc1de
DDM
9288 else:
9289 source_dir = None
33b0e0c0 9290
da249835
LB
9291 if networkd_bin is None or resolved_bin is None or timesyncd_bin is None:
9292 print("networkd tests require networkd/resolved/timesyncd to be enabled")
9293 sys.exit(77)
9294
9c1ae484
YW
9295 use_valgrind = ns.use_valgrind
9296 enable_debug = ns.enable_debug
94c03122 9297 asan_options = ns.asan_options
fa4c6095 9298 lsan_options = ns.lsan_options
94c03122 9299 ubsan_options = ns.ubsan_options
c45174f0 9300 with_coverage = ns.with_coverage or "COVERAGE_BUILD_DIR" in os.environ
e92d7b7d 9301 show_journal = ns.show_journal
9c1ae484
YW
9302
9303 if use_valgrind:
b05c4d6b
YW
9304 # Do not forget the trailing space.
9305 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
9306
9307 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
9308 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
9309 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
9310 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
9311 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
9c1ae484 9312
9dcdf16b
YW
9313 if build_dir:
9314 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
9315 else:
9316 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
9317
94c03122 9318 if asan_options:
a962d857 9319 env.update({'ASAN_OPTIONS': asan_options})
fa4c6095 9320 if lsan_options:
a962d857 9321 env.update({'LSAN_OPTIONS': lsan_options})
94c03122 9322 if ubsan_options:
a962d857 9323 env.update({'UBSAN_OPTIONS': ubsan_options})
b05c4d6b
YW
9324 if use_valgrind:
9325 env.update({'SYSTEMD_MEMPOOL': '0'})
9c1ae484 9326
163d095f
YW
9327 wait_online_env = env.copy()
9328 if enable_debug:
a962d857 9329 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
163d095f 9330
15c1a785
DDM
9331 unittest.main(
9332 verbosity=3,
9333 argv=[
9334 sys.argv[0],
9335 *unknown_args,
9336 *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else [])
9337 ],
9338 )