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