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