]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / test / test-network / systemd-networkd-tests.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # systemd-networkd tests
4
5 # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
6 # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
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 # run0 ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
13 #
14 # Similarly, other individual tests can be run, e.g.:
15 #
16 # run0 ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
17 #
18 # To run the test with the executables (systemd-networkd, networkctl, systemd-udevd and so on)
19 # in your build directory, --build-dir=/path/to/build/ option can be used:
20 #
21 # run0 ./systemd-networkd-tests.py --build-dir=/path/to/build NetworkdNetworkTests.test_address_static
22 #
23 # Note, unlike the long getopt option handling, the path must be specified after '=', rather than space.
24 # Otherwise the path is recognized as a test case, and the test run will fail.
25
26 import argparse
27 import datetime
28 import enum
29 import errno
30 import itertools
31 import ipaddress
32 import json
33 import os
34 import pathlib
35 import random
36 import re
37 import shutil
38 import signal
39 import socket
40 import struct
41 import subprocess
42 import sys
43 import tempfile
44 import time
45 import unittest
46
47 import psutil
48
49 network_unit_dir = '/run/systemd/network'
50 networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
51 networkd_ci_temp_dir = '/run/networkd-ci'
52 udev_rules_dir = '/run/udev/rules.d'
53 credstore_dir = '/run/credstore'
54
55 dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
56 dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
57 dnsmasq_lease_file = '/run/networkd-ci/test-dnsmasq.lease'
58
59 isc_dhcpd_pid_file = '/run/networkd-ci/test-isc-dhcpd.pid'
60 isc_dhcpd_lease_file = '/run/networkd-ci/test-isc-dhcpd.lease'
61
62 radvd_pid_file = '/run/networkd-ci/test-radvd.pid'
63
64 systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
65 which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
66
67 networkd_bin = shutil.which('systemd-networkd', path=which_paths)
68 resolved_bin = shutil.which('systemd-resolved', path=which_paths)
69 timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
70 wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
71 networkctl_bin = shutil.which('networkctl', path=which_paths)
72 resolvectl_bin = shutil.which('resolvectl', path=which_paths)
73 timedatectl_bin = shutil.which('timedatectl', path=which_paths)
74 udevadm_bin = shutil.which('udevadm', path=which_paths)
75 test_ndisc_send = None
76 build_dir = None
77 source_dir = None
78
79 use_valgrind = False
80 valgrind_cmd = ''
81 enable_debug = True
82 env = {}
83 wait_online_env = {}
84 asan_options = None
85 lsan_options = None
86 ubsan_options = None
87 with_coverage = False
88 show_journal = True # When true, show journal on stopping networkd.
89
90 active_units = []
91 protected_links = {
92 'erspan0',
93 'gre0',
94 'gretap0',
95 'ifb0',
96 'ifb1',
97 'ip6_vti0',
98 'ip6gre0',
99 'ip6tnl0',
100 'ip_vti0',
101 'lo',
102 'sit0',
103 'tunl0',
104 }
105 saved_routes = None
106 saved_ipv4_rules = None
107 saved_ipv6_rules = None
108 saved_timezone = None
109
110 def rm_f(path):
111 if os.path.exists(path):
112 os.remove(path)
113
114 def rm_rf(path):
115 shutil.rmtree(path, ignore_errors=True)
116
117 def cp(src, dst):
118 shutil.copy(src, dst)
119
120 def cp_r(src, dst):
121 shutil.copytree(src, dst, copy_function=shutil.copy, dirs_exist_ok=True)
122
123 def mkdir_p(path):
124 os.makedirs(path, exist_ok=True)
125
126 def touch(path):
127 pathlib.Path(path).touch()
128
129 # pylint: disable=R1710
130 def check_output(*command, **kwargs):
131 # This checks the result and returns stdout (and stderr) on success.
132 command = command[0].split() + list(command[1:])
133 ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
134 if ret.returncode == 0:
135 return ret.stdout.rstrip()
136 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
137 print(ret.stdout)
138 ret.check_returncode()
139
140 def call(*command, **kwargs):
141 # This returns returncode. stdout and stderr are merged and shown in console
142 command = command[0].split() + list(command[1:])
143 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode
144
145 def call_check(*command, **kwargs):
146 # Same as call() above, but it triggers CalledProcessError if rc != 0
147 command = command[0].split() + list(command[1:])
148 return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).check_returncode()
149
150 def call_quiet(*command, **kwargs):
151 command = command[0].split() + list(command[1:])
152 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
153
154 def run(*command, **kwargs):
155 # This returns CompletedProcess instance.
156 command = command[0].split() + list(command[1:])
157 return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
158
159 def check_json(string):
160 try:
161 json.loads(string)
162 except json.JSONDecodeError:
163 print(f"String is not a valid JSON: '{string}'")
164 raise
165
166 def is_module_available(*module_names):
167 for module_name in module_names:
168 lsmod_output = check_output('lsmod')
169 module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
170 if not module_re.search(lsmod_output) and call_quiet('modprobe', module_name) != 0:
171 return False
172 return True
173
174 def expectedFailureIfModuleIsNotAvailable(*module_names):
175 def f(func):
176 return func if is_module_available(*module_names) else unittest.expectedFailure(func)
177
178 return f
179
180 def expectedFailureIfERSPANv0IsNotSupported():
181 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
182 def f(func):
183 rc = call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 0')
184 remove_link('erspan99')
185 return func if rc == 0 else unittest.expectedFailure(func)
186
187 return f
188
189 def expectedFailureIfERSPANv2IsNotSupported():
190 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
191 def f(func):
192 rc = call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 2')
193 remove_link('erspan99')
194 return func if rc == 0 else unittest.expectedFailure(func)
195
196 return f
197
198 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
199 def f(func):
200 rc = call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
201 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
202 return func if rc == 0 else unittest.expectedFailure(func)
203
204 return f
205
206 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
207 def f(func):
208 # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols.
209 # Hence. here we use explicit number: 6 == tcp.
210 rc = call_quiet('ip rule add not from 192.168.100.19 ipproto 6 table 7')
211 call_quiet('ip rule del not from 192.168.100.19 ipproto 6 table 7')
212 return func if rc == 0 else unittest.expectedFailure(func)
213
214 return f
215
216 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
217 def f(func):
218 supported = False
219 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
220 ret = run('ip rule list from 192.168.100.19 table 7')
221 supported = ret.returncode == 0 and 'uidrange 200-300' in ret.stdout
222 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
223 return func if supported else unittest.expectedFailure(func)
224
225 return f
226
227 def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
228 def f(func):
229 rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev')
230 call_quiet('ip rule del not from 192.168.100.19 l3mdev')
231 return func if rc == 0 else unittest.expectedFailure(func)
232
233 return f
234
235 def expectedFailureIfNexthopIsNotAvailable():
236 def f(func):
237 rc = call_quiet('ip nexthop list')
238 return func if rc == 0 else unittest.expectedFailure(func)
239
240 return f
241
242 def expectedFailureIfRTA_VIAIsNotSupported():
243 def f(func):
244 call_quiet('ip link add dummy98 type dummy')
245 call_quiet('ip link set up dev dummy98')
246 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
247 rc = call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
248 remove_link('dummy98')
249 return func if rc == 0 else unittest.expectedFailure(func)
250
251 return f
252
253 def expectedFailureIfAlternativeNameIsNotAvailable():
254 def f(func):
255 call_quiet('ip link add dummy98 type dummy')
256 supported = \
257 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
258 call_quiet('ip link show dev hogehogehogehogehoge') == 0
259 remove_link('dummy98')
260 return func if supported else unittest.expectedFailure(func)
261
262 return f
263
264 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
265 def f(func):
266 def finalize(func, supported):
267 call_quiet('rmmod netdevsim')
268 return func if supported else unittest.expectedFailure(func)
269
270 call_quiet('rmmod netdevsim')
271 if call_quiet('modprobe netdevsim') != 0:
272 return finalize(func, False)
273
274 try:
275 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
276 f.write('99 1')
277 except OSError:
278 return finalize(func, False)
279
280 return finalize(func, os.path.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
281
282 return f
283
284 def expectedFailureIfKernelReturnsInvalidFlags():
285 '''
286 This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
287 fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
288 '''
289 def f(func):
290 call_quiet('ip link add dummy98 type dummy')
291 call_quiet('ip link set up dev dummy98')
292 call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute')
293 output = check_output('ip address show dev dummy98')
294 remove_link('dummy98')
295 return func if 'noprefixroute' in output else unittest.expectedFailure(func)
296
297 return f
298
299 # pylint: disable=C0415
300 def compare_kernel_version(min_kernel_version):
301 try:
302 import platform
303 from packaging import version
304 except ImportError:
305 print('Failed to import either platform or packaging module, assuming the comparison failed')
306 return False
307
308 # Get only the actual kernel version without any build/distro/arch stuff
309 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
310 kver = platform.release().split('-')[0]
311 # Get also rid of '+'
312 kver = kver.split('+')[0]
313
314 return version.parse(kver) >= version.parse(min_kernel_version)
315
316 def copy_network_unit(*units, copy_dropins=True):
317 """
318 Copy networkd unit files into the testbed.
319
320 Any networkd unit file type can be specified, as well as drop-in files.
321
322 By default, all drop-ins for a specified unit file are copied in;
323 to avoid that specify dropins=False.
324
325 When a drop-in file is specified, its unit file is also copied in automatically.
326 """
327 has_link = False
328 mkdir_p(network_unit_dir)
329 for unit in units:
330 if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
331 cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
332
333 if unit.endswith('.conf'):
334 dropin = unit
335 unit = os.path.dirname(dropin).rstrip('.d')
336 dropindir = os.path.join(network_unit_dir, unit + '.d')
337 mkdir_p(dropindir)
338 cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
339
340 cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
341
342 if unit.endswith('.link'):
343 has_link = True
344
345 if has_link:
346 udevadm_reload()
347
348 def copy_credential(src, target):
349 mkdir_p(credstore_dir)
350 cp(os.path.join(networkd_ci_temp_dir, src),
351 os.path.join(credstore_dir, target))
352
353 def remove_network_unit(*units):
354 """
355 Remove previously copied unit files from the testbed.
356
357 Drop-ins will be removed automatically.
358 """
359 has_link = False
360 for unit in units:
361 rm_f(os.path.join(network_unit_dir, unit))
362 rm_rf(os.path.join(network_unit_dir, unit + '.d'))
363
364 if unit.endswith('.link') or unit.endswith('.link.d'):
365 has_link = True
366
367 if has_link:
368 udevadm_reload()
369
370 def touch_network_unit(*units):
371 for unit in units:
372 touch(os.path.join(network_unit_dir, unit))
373
374 def clear_network_units():
375 has_link = False
376 if os.path.exists(network_unit_dir):
377 units = os.listdir(network_unit_dir)
378 for unit in units:
379 if unit.endswith('.link') or unit.endswith('.link.d'):
380 has_link = True
381
382 rm_rf(network_unit_dir)
383
384 if has_link:
385 udevadm_reload()
386
387 def copy_networkd_conf_dropin(*dropins):
388 """Copy networkd.conf dropin files into the testbed."""
389 mkdir_p(networkd_conf_dropin_dir)
390 for dropin in dropins:
391 cp(os.path.join(networkd_ci_temp_dir, dropin), networkd_conf_dropin_dir)
392
393 def remove_networkd_conf_dropin(*dropins):
394 """Remove previously copied networkd.conf dropin files from the testbed."""
395 for dropin in dropins:
396 rm_f(os.path.join(networkd_conf_dropin_dir, dropin))
397
398 def clear_networkd_conf_dropins():
399 rm_rf(networkd_conf_dropin_dir)
400
401 def setup_systemd_udev_rules():
402 if not build_dir and not source_dir:
403 return
404
405 mkdir_p(udev_rules_dir)
406
407 for path in [build_dir, source_dir]:
408 if not path:
409 continue
410
411 path = os.path.join(path, "rules.d")
412 print(f"Copying udev rules from {path} to {udev_rules_dir}")
413
414 for rule in os.listdir(path):
415 if not rule.endswith(".rules"):
416 continue
417 cp(os.path.join(path, rule), udev_rules_dir)
418
419 def clear_networkd_state_files():
420 rm_rf('/var/lib/systemd/network/')
421
422 def copy_udev_rule(*rules):
423 """Copy udev rules"""
424 mkdir_p(udev_rules_dir)
425 for rule in rules:
426 cp(os.path.join(networkd_ci_temp_dir, rule), udev_rules_dir)
427
428 def remove_udev_rule(*rules):
429 """Remove previously copied udev rules"""
430 for rule in rules:
431 rm_f(os.path.join(udev_rules_dir, rule))
432
433 def clear_udev_rules():
434 rm_rf(udev_rules_dir)
435
436 def save_active_units():
437 for u in ['systemd-networkd.socket', 'systemd-networkd.service',
438 'systemd-resolved.service', 'systemd-timesyncd.service',
439 'firewalld.service']:
440 if call(f'systemctl is-active --quiet {u}') == 0:
441 call(f'systemctl stop {u}')
442 active_units.append(u)
443
444 def restore_active_units():
445 if 'systemd-networkd.socket' in active_units:
446 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
447 for u in active_units:
448 call(f'systemctl restart {u}')
449
450 def create_unit_dropin(unit, contents):
451 mkdir_p(f'/run/systemd/system/{unit}.d')
452 with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
453 f.write('\n'.join(contents))
454
455 def create_service_dropin(service, command, additional_settings=None):
456 drop_in = ['[Service]']
457 if command:
458 drop_in += [
459 'ExecStart=',
460 f'ExecStart={valgrind_cmd}{command}',
461 ]
462 if enable_debug:
463 drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
464 if asan_options:
465 drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
466 if lsan_options:
467 drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
468 if ubsan_options:
469 drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
470 if asan_options or lsan_options or ubsan_options:
471 drop_in += ['SystemCallFilter=']
472 if use_valgrind or asan_options or lsan_options or ubsan_options:
473 drop_in += ['MemoryDenyWriteExecute=no']
474 if use_valgrind:
475 drop_in += [
476 'Environment=SYSTEMD_MEMPOOL=0',
477 'PrivateTmp=yes',
478 ]
479 if with_coverage:
480 drop_in += [
481 'ProtectSystem=no',
482 'ProtectHome=no',
483 ]
484 if additional_settings:
485 drop_in += additional_settings
486
487 create_unit_dropin(f'{service}.service', drop_in)
488
489 def setup_system_units():
490 if build_dir or source_dir:
491 mkdir_p('/run/systemd/system/')
492
493 for unit in [
494 'systemd-networkd.service',
495 'systemd-networkd.socket',
496 'systemd-networkd-persistent-storage.service',
497 'systemd-resolved.service',
498 'systemd-timesyncd.service',
499 'systemd-udevd.service',
500 ]:
501 for path in [build_dir, source_dir]:
502 if not path:
503 continue
504
505 fullpath = os.path.join(os.path.join(path, "units"), unit)
506 if os.path.exists(fullpath):
507 print(f"Copying unit file from {fullpath} to /run/systemd/system/")
508 cp(fullpath, '/run/systemd/system/')
509 break
510
511 create_service_dropin('systemd-networkd', networkd_bin,
512 ['[Service]',
513 'Restart=no',
514 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
515 '[Unit]',
516 'StartLimitIntervalSec=0'])
517 create_service_dropin('systemd-resolved', resolved_bin)
518 create_service_dropin('systemd-timesyncd', timesyncd_bin)
519
520 # TODO: also run udevd with sanitizers, valgrind, or coverage
521 create_unit_dropin(
522 'systemd-udevd.service',
523 [
524 '[Service]',
525 'ExecStart=',
526 f'ExecStart=@{udevadm_bin} systemd-udevd',
527 ]
528 )
529 create_unit_dropin(
530 'systemd-networkd.socket',
531 [
532 '[Unit]',
533 'StartLimitIntervalSec=0',
534 ]
535 )
536 create_unit_dropin(
537 'systemd-networkd-persistent-storage.service',
538 [
539 '[Unit]',
540 'StartLimitIntervalSec=0',
541 '[Service]',
542 'ExecStart=',
543 f'ExecStart={networkctl_bin} persistent-storage yes',
544 'ExecStop=',
545 f'ExecStop={networkctl_bin} persistent-storage no',
546 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '',
547 ]
548 )
549
550 check_output('systemctl daemon-reload')
551 print(check_output('systemctl cat systemd-networkd.service'))
552 print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
553 print(check_output('systemctl cat systemd-resolved.service'))
554 print(check_output('systemctl cat systemd-timesyncd.service'))
555 print(check_output('systemctl cat systemd-udevd.service'))
556 check_output('systemctl restart systemd-resolved.service')
557 check_output('systemctl restart systemd-timesyncd.service')
558 check_output('systemctl restart systemd-udevd.service')
559
560 def clear_system_units():
561 def rm_unit(name):
562 rm_f(f'/run/systemd/system/{name}')
563 rm_rf(f'/run/systemd/system/{name}.d')
564
565 rm_unit('systemd-networkd.service')
566 rm_unit('systemd-networkd.socket')
567 rm_unit('systemd-networkd-persistent-storage.service')
568 rm_unit('systemd-resolved.service')
569 rm_unit('systemd-timesyncd.service')
570 rm_unit('systemd-udevd.service')
571 check_output('systemctl daemon-reload')
572 check_output('systemctl restart systemd-udevd.service')
573
574 def link_exists(*links):
575 for link in links:
576 if call_quiet(f'ip link show {link}') != 0:
577 return False
578 return True
579
580 def link_resolve(link):
581 return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0]
582
583 def remove_link(*links, protect=False):
584 for link in links:
585 if protect and link in protected_links:
586 continue
587 if link_exists(link):
588 call(f'ip link del dev {link}')
589
590 def save_existing_links():
591 links = os.listdir('/sys/class/net')
592 for link in links:
593 if link_exists(link):
594 protected_links.add(link)
595
596 print('### The following links will be protected:')
597 print(', '.join(sorted(list(protected_links))))
598
599 def unmanage_existing_links():
600 mkdir_p(network_unit_dir)
601
602 with open(os.path.join(network_unit_dir, '00-unmanaged.network'), mode='w', encoding='utf-8') as f:
603 f.write('[Match]\n')
604 for link in protected_links:
605 f.write(f'Name={link}\n')
606 f.write('\n[Link]\nUnmanaged=yes\n')
607
608 def flush_links():
609 links = os.listdir('/sys/class/net')
610 remove_link(*links, protect=True)
611
612 def flush_nexthops():
613 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
614 # Hence, we cannot restore nexthops in a simple way.
615 # Let's assume there is no nexthop used in the system
616 call_quiet('ip nexthop flush')
617
618 def save_routes():
619 # pylint: disable=global-statement
620 global saved_routes
621 saved_routes = check_output('ip route show table all')
622 print('### The following routes will be protected:')
623 print(saved_routes)
624
625 def flush_routes():
626 have = False
627 output = check_output('ip route show table all')
628 for line in output.splitlines():
629 if line in saved_routes:
630 continue
631 if 'proto kernel' in line:
632 continue
633 if ' dev ' in line and not ' dev lo ' in line:
634 continue
635 if not have:
636 have = True
637 print('### Removing routes that did not exist when the test started.')
638 print(f'# {line}')
639 call(f'ip route del {line}')
640
641 def save_routing_policy_rules():
642 # pylint: disable=global-statement
643 global saved_ipv4_rules, saved_ipv6_rules
644 def save(ipv):
645 output = check_output(f'ip -{ipv} rule show')
646 print(f'### The following IPv{ipv} routing policy rules will be protected:')
647 print(output)
648 return output
649
650 saved_ipv4_rules = save(4)
651 saved_ipv6_rules = save(6)
652
653 def flush_routing_policy_rules():
654 def flush(ipv, saved_rules):
655 have = False
656 output = check_output(f'ip -{ipv} rule show')
657 for line in output.splitlines():
658 if line in saved_rules:
659 continue
660 if not have:
661 have = True
662 print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
663 print(f'# {line}')
664 words = line.replace('lookup [l3mdev-table]', 'l3mdev').replace('[detached]', '').split()
665 priority = words[0].rstrip(':')
666 call(f'ip -{ipv} rule del priority {priority} ' + ' '.join(words[1:]))
667
668 flush(4, saved_ipv4_rules)
669 flush(6, saved_ipv6_rules)
670
671 def flush_fou_ports():
672 ret = run('ip fou show')
673 if ret.returncode != 0:
674 return # fou may not be supported
675 for line in ret.stdout.splitlines():
676 port = line.split()[1]
677 call(f'ip fou del port {port}')
678
679 def flush_l2tp_tunnels():
680 tids = []
681 ret = run('ip l2tp show tunnel')
682 if ret.returncode != 0:
683 return # l2tp may not be supported
684 for line in ret.stdout.splitlines():
685 words = line.split()
686 if words[0] == 'Tunnel':
687 tid = words[1].rstrip(',')
688 call(f'ip l2tp del tunnel tunnel_id {tid}')
689 tids.append(tid)
690
691 # Removing L2TP tunnel is asynchronous and slightly takes a time.
692 for tid in tids:
693 for _ in range(50):
694 r = run(f'ip l2tp show tunnel tunnel_id {tid}')
695 if r.returncode != 0 or len(r.stdout.rstrip()) == 0:
696 break
697 time.sleep(.2)
698 else:
699 print(f'Cannot remove L2TP tunnel {tid}, ignoring.')
700
701 def save_timezone():
702 # pylint: disable=global-statement
703 global saved_timezone
704 r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
705 if r.returncode == 0:
706 saved_timezone = r.stdout.rstrip()
707 print(f'### Saved timezone: {saved_timezone}')
708
709 def restore_timezone():
710 if saved_timezone:
711 call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
712
713 def read_link_attr(*args):
714 with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
715 return f.readline().strip()
716
717 def read_manager_state_file():
718 with open('/run/systemd/netif/state', encoding='utf-8') as f:
719 return f.read()
720
721 def read_link_state_file(link):
722 ifindex = read_link_attr(link, 'ifindex')
723 path = os.path.join('/run/systemd/netif/links', ifindex)
724 with open(path, encoding='utf-8') as f:
725 return f.read()
726
727 def read_ip_sysctl_attr(link, attribute, ipv):
728 with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f:
729 return f.readline().strip()
730
731 def read_ip_neigh_sysctl_attr(link, attribute, ipv):
732 with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f:
733 return f.readline().strip()
734
735 def read_ipv6_sysctl_attr(link, attribute):
736 return read_ip_sysctl_attr(link, attribute, 'ipv6')
737
738 def read_ipv6_neigh_sysctl_attr(link, attribute):
739 return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6')
740
741 def read_ipv4_sysctl_attr(link, attribute):
742 return read_ip_sysctl_attr(link, attribute, 'ipv4')
743
744 def read_mpls_sysctl_attr(link, attribute):
745 return read_ip_sysctl_attr(link, attribute, 'mpls')
746
747 def stop_by_pid_file(pid_file):
748 if not os.path.exists(pid_file):
749 return
750 with open(pid_file, 'r', encoding='utf-8') as f:
751 pid = f.read().rstrip(' \t\r\n\0')
752 os.kill(int(pid), signal.SIGTERM)
753 for _ in range(25):
754 try:
755 os.kill(int(pid), 0)
756 print(f"PID {pid} is still alive, waiting...")
757 time.sleep(.2)
758 except OSError as e:
759 if e.errno == errno.ESRCH:
760 break
761 print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
762 rm_f(pid_file)
763
764 def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
765 b = bytes()
766 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
767 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
768 ipv4 = ipaddress.IPv4Address
769 class SvcParam(enum.Enum):
770 ALPN = 1
771 DOHPATH = 7
772
773 data = pyton(prio)
774
775 adn = adn.rstrip('.') + '.'
776 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')))
777
778 if not addrs: # adn-only mode
779 return pack(data, 2)
780
781 data += pack(b.join(ipv4(addr).packed for addr in addrs))
782 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
783 if dohpath is not None:
784 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
785
786 return pack(data, 2)
787
788 def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
789 b = bytes()
790 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
791 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
792 ipv6 = ipaddress.IPv6Address
793 class SvcParam(enum.Enum):
794 ALPN = 1
795 DOHPATH = 7
796
797 data = pyton(prio)
798
799 adn = adn.rstrip('.') + '.'
800 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')), 2)
801
802 if not addrs: # adn-only mode
803 return data
804
805 data += pack(b.join(ipv6(addr).packed for addr in addrs), 2)
806 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
807 if dohpath is not None:
808 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
809
810 return data
811
812 def start_dnsmasq(*additional_options, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
813 if ra_mode:
814 ra_mode = f',{ra_mode}'
815 else:
816 ra_mode = ''
817
818 command = (
819 'dnsmasq',
820 f'--log-facility={dnsmasq_log_file}',
821 '--log-queries=extra',
822 '--log-dhcp',
823 f'--pid-file={dnsmasq_pid_file}',
824 '--conf-file=/dev/null',
825 '--bind-interfaces',
826 f'--interface={interface}',
827 f'--dhcp-leasefile={dnsmasq_lease_file}',
828 '--enable-ra',
829 f'--dhcp-range={ipv6_range}{ra_mode},2m',
830 f'--dhcp-range={ipv4_range},2m',
831 '--dhcp-option=option:mtu,1492',
832 f'--dhcp-option=option:router,{ipv4_router}',
833 '--port=0',
834 '--no-resolv',
835 ) + additional_options
836 check_output(*command)
837
838 def stop_dnsmasq():
839 stop_by_pid_file(dnsmasq_pid_file)
840 rm_f(dnsmasq_lease_file)
841 rm_f(dnsmasq_log_file)
842
843 def read_dnsmasq_log_file():
844 with open(dnsmasq_log_file, encoding='utf-8') as f:
845 return f.read()
846
847 def start_isc_dhcpd(conf_file, ipv, interface='veth-peer'):
848 conf_file_path = os.path.join(networkd_ci_temp_dir, conf_file)
849 isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
850 touch(isc_dhcpd_lease_file)
851 check_output(isc_dhcpd_command)
852
853 def stop_isc_dhcpd():
854 stop_by_pid_file(isc_dhcpd_pid_file)
855 rm_f(isc_dhcpd_lease_file)
856
857 def get_dbus_link_path(link):
858 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
859 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
860 'GetLinkByName', 's', link])
861
862 assert out.startswith(b'io ')
863 out = out.strip()
864 assert out.endswith(b'"')
865 out = out.decode()
866 return out[:-1].split('"')[1]
867
868 def get_dhcp_client_state(link, family):
869 link_path = get_dbus_link_path(link)
870
871 out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
872 link_path, f'org.freedesktop.network1.DHCPv{family}Client', 'State'])
873 assert out.startswith(b's "')
874 out = out.strip()
875 assert out.endswith(b'"')
876 return out[3:-1].decode()
877
878 def get_dhcp4_client_state(link):
879 return get_dhcp_client_state(link, '4')
880
881 def get_dhcp6_client_state(link):
882 return get_dhcp_client_state(link, '6')
883
884 def get_link_description(link):
885 link_path = get_dbus_link_path(link)
886
887 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
888 link_path, 'org.freedesktop.network1.Link', 'Describe'])
889 assert out.startswith(b's "')
890 out = out.strip()
891 assert out.endswith(b'"')
892 json_raw = out[2:].decode()
893 check_json(json_raw)
894 description = json.loads(json_raw) # Convert from escaped sequences to json
895 check_json(description)
896 return json.loads(description) # Now parse the json
897
898 def start_radvd(*additional_options, config_file):
899 config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file)
900 command = (
901 'radvd',
902 f'--pidfile={radvd_pid_file}',
903 f'--config={config_file_path}',
904 '--logmethod=stderr',
905 ) + additional_options
906 check_output(*command)
907
908 def stop_radvd():
909 stop_by_pid_file(radvd_pid_file)
910
911 def radvd_check_config(config_file):
912 if not shutil.which('radvd'):
913 print('radvd is not installed, assuming the config check failed')
914 return False
915
916 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
917 # set up (one instance is @unittest.skipX())
918 config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file)
919 return call(f'radvd --config={config_file_path} --configtest') == 0
920
921 def networkd_invocation_id():
922 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
923
924 def networkd_pid():
925 return check_output('systemctl show --value -p MainPID systemd-networkd.service')
926
927 def read_networkd_log(invocation_id=None, since=None):
928 if not invocation_id:
929 invocation_id = networkd_invocation_id()
930 command = [
931 'journalctl',
932 '--no-hostname',
933 '--output=short-monotonic',
934 f'_SYSTEMD_INVOCATION_ID={invocation_id}',
935 ]
936 if since:
937 command.append(f'--since={since}')
938 check_output('journalctl --sync')
939 return check_output(*command)
940
941 def networkd_is_failed():
942 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
943
944 def stop_networkd(show_logs=True, check_failed=True):
945 global show_journal
946 show_logs = show_logs and show_journal
947 if show_logs:
948 invocation_id = networkd_invocation_id()
949
950 if check_failed:
951 check_output('systemctl stop systemd-networkd.socket')
952 check_output('systemctl stop systemd-networkd.service')
953 else:
954 call('systemctl stop systemd-networkd.socket')
955 call('systemctl stop systemd-networkd.service')
956
957 if show_logs:
958 print(read_networkd_log(invocation_id))
959
960 # Check if networkd exits cleanly.
961 if check_failed:
962 assert not networkd_is_failed()
963
964 def start_networkd():
965 check_output('systemctl start systemd-networkd')
966 invocation_id = networkd_invocation_id()
967 pid = networkd_pid()
968 print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
969
970 def restart_networkd(show_logs=True):
971 global show_journal
972 show_logs = show_logs and show_journal
973 if show_logs:
974 invocation_id = networkd_invocation_id()
975 check_output('systemctl restart systemd-networkd.service')
976 if show_logs:
977 print(read_networkd_log(invocation_id))
978
979 invocation_id = networkd_invocation_id()
980 pid = networkd_pid()
981 print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
982
983 def networkd_pid():
984 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
985
986 def networkctl(*args):
987 # Do not call networkctl if networkd is in failed state.
988 # Otherwise, networkd may be restarted and we may get wrong results.
989 assert not networkd_is_failed()
990 return check_output(*(networkctl_cmd + list(args)), env=env)
991
992 def networkctl_status(*args):
993 return networkctl('-n', '0', 'status', *args)
994
995 def networkctl_json(*args):
996 return networkctl('--json=short', 'status', *args)
997
998 def networkctl_reconfigure(*links):
999 networkctl('reconfigure', *links)
1000
1001 def networkctl_reload():
1002 networkctl('reload')
1003
1004 def resolvectl(*args):
1005 return check_output(*(resolvectl_cmd + list(args)), env=env)
1006
1007 def timedatectl(*args):
1008 return check_output(*(timedatectl_cmd + list(args)), env=env)
1009
1010 def udevadm(*args):
1011 return check_output(*(udevadm_cmd + list(args)))
1012
1013 def udevadm_reload():
1014 udevadm('control', '--reload')
1015
1016 def udevadm_trigger(*args, action='add'):
1017 udevadm('trigger', '--settle', f'--action={action}', *args)
1018
1019 def setup_common():
1020 # Protect existing links
1021 unmanage_existing_links()
1022
1023 # We usually show something in each test. So, let's break line to make the title of a test and output
1024 # from the test mixed. Then, flush stream buffer and journals.
1025 print()
1026 sys.stdout.flush()
1027 check_output('journalctl --sync')
1028
1029 def tear_down_common():
1030 # 1. stop DHCP/RA servers
1031 stop_dnsmasq()
1032 stop_isc_dhcpd()
1033 stop_radvd()
1034
1035 # 2. remove modules
1036 call_quiet('rmmod netdevsim')
1037 call_quiet('rmmod sch_teql')
1038
1039 # 3. remove network namespace
1040 call_quiet('ip netns del ns99')
1041
1042 # 4. remove links
1043 flush_l2tp_tunnels()
1044 flush_links()
1045
1046 # 5. stop networkd
1047 stop_networkd(check_failed=False)
1048
1049 # 6. remove configs
1050 clear_network_units()
1051 clear_networkd_conf_dropins()
1052 clear_networkd_state_files()
1053
1054 # 7. flush settings
1055 flush_fou_ports()
1056 flush_nexthops()
1057 flush_routing_policy_rules()
1058 flush_routes()
1059
1060 # 8. flush stream buffer and journals to make not any output from the test with the next one
1061 sys.stdout.flush()
1062 check_output('journalctl --sync')
1063
1064 # 9. check the status of networkd
1065 assert not networkd_is_failed()
1066
1067 def setUpModule():
1068 rm_rf(networkd_ci_temp_dir)
1069 cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir)
1070
1071 clear_network_units()
1072 clear_networkd_conf_dropins()
1073 clear_networkd_state_files()
1074 clear_udev_rules()
1075
1076 setup_systemd_udev_rules()
1077 copy_udev_rule('00-debug-net.rules')
1078
1079 # Save current state
1080 save_active_units()
1081 save_existing_links()
1082 save_routes()
1083 save_routing_policy_rules()
1084 save_timezone()
1085
1086 setup_system_units()
1087
1088 def tearDownModule():
1089 rm_rf(networkd_ci_temp_dir)
1090 clear_udev_rules()
1091 clear_network_units()
1092 clear_networkd_conf_dropins()
1093 clear_networkd_state_files()
1094
1095 restore_timezone()
1096
1097 clear_system_units()
1098 restore_active_units()
1099
1100 # Flush stream buffer and journals before showing the test summary.
1101 sys.stdout.flush()
1102 check_output('journalctl --sync')
1103
1104 class Utilities():
1105 # pylint: disable=no-member
1106
1107 def check_link_exists(self, *link, expected=True):
1108 if expected:
1109 self.assertTrue(link_exists(*link))
1110 else:
1111 self.assertFalse(link_exists(*link))
1112
1113 def check_link_attr(self, *args):
1114 self.assertEqual(read_link_attr(*args[:-1]), args[-1])
1115
1116 def check_bridge_port_attr(self, master, port, attribute, expected, allow_enoent=False):
1117 path = os.path.join('/sys/devices/virtual/net', master, 'lower_' + port, 'brport', attribute)
1118 if allow_enoent and not os.path.exists(path):
1119 return
1120 with open(path, encoding='utf-8') as f:
1121 self.assertEqual(f.readline().strip(), expected)
1122
1123 def check_ipv4_sysctl_attr(self, link, attribute, expected):
1124 self.assertEqual(read_ipv4_sysctl_attr(link, attribute), expected)
1125
1126 def check_ipv6_sysctl_attr(self, link, attribute, expected):
1127 self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected)
1128
1129 def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
1130 self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
1131
1132 def check_mpls_sysctl_attr(self, link, attribute, expected):
1133 self.assertEqual(read_mpls_sysctl_attr(link, attribute), expected)
1134
1135 def wait_links(self, *links, trial=40):
1136 for _ in range(trial):
1137 if link_exists(*links):
1138 break
1139 time.sleep(0.5)
1140 else:
1141 self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links)))
1142
1143 def wait_activated(self, link, state='down', trial=40):
1144 # wait for the interface is activated.
1145 needle = f'{link}: Bringing link {state}'
1146 flag = state.upper()
1147 self.wait_links(link, trial=trial)
1148 self.check_networkd_log(needle, trial=trial)
1149 for _ in range(trial):
1150 if flag in check_output(f'ip link show {link}'):
1151 break
1152 time.sleep(0.5)
1153 else:
1154 self.fail(f'Timed out waiting for {link} activated.')
1155
1156 def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
1157 """Wait for the link to reach the specified operstate and/or setup state.
1158
1159 Specify None or '' for either operstate or setup_state to ignore that state.
1160 This will recheck until the state conditions are met or the timeout expires.
1161
1162 If the link successfully matches the requested state, this returns True.
1163 If this times out waiting for the link to match, the behavior depends on the
1164 'fail_assert' parameter; if True, this causes a test assertion failure,
1165 otherwise this returns False. The default is to cause assertion failure.
1166
1167 Note that this function matches on *exactly* the given operstate and setup_state.
1168 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1169 """
1170 if not operstate:
1171 operstate = r'\S+'
1172 if not setup_state:
1173 setup_state = r'\S+'
1174
1175 for _ in range(setup_timeout * 2):
1176 if not link_exists(link):
1177 time.sleep(0.5)
1178 continue
1179 output = networkctl_status(link)
1180 if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
1181 return True
1182 time.sleep(0.5)
1183
1184 if fail_assert:
1185 self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1186 return False
1187
1188 def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5, bool_dns=False):
1189 """Wait for the links to reach the specified operstate and/or setup state.
1190
1191 This is similar to wait_operstate() but can be used for multiple links,
1192 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1193 The operstate should be specified in the link name, like 'eth0:degraded'.
1194 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1195
1196 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1197 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1198
1199 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1200 If this is set, no setup_state checks are done.
1201
1202 Set 'bool_dns' to True to wait for DNS servers to be accessible.
1203
1204 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1205 This is applied only for the operational state 'degraded' or above.
1206
1207 Note that this function waits for the links to reach *or exceed* the given operstate.
1208 However, the setup_state, if specified, must be matched *exactly*.
1209
1210 This returns if the links reached the requested operstate/setup_state; otherwise it
1211 raises CalledProcessError or fails test assertion.
1212 """
1213 args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + [f'--ignore={link}' for link in protected_links]
1214 if bool_any:
1215 args += ['--any']
1216 if bool_dns:
1217 args += ['--dns']
1218 if ipv4:
1219 args += ['--ipv4']
1220 if ipv6:
1221 args += ['--ipv6']
1222 try:
1223 check_output(*args, env=wait_online_env)
1224 except subprocess.CalledProcessError:
1225 if networkd_is_failed():
1226 print('!!!!! systemd-networkd.service is failed !!!!!')
1227 call('systemctl status systemd-networkd.service')
1228 else:
1229 # show detailed status on failure
1230 for link in links_with_operstate:
1231 name = link.split(':')[0]
1232 if link_exists(name):
1233 print(networkctl_status(name))
1234 else:
1235 print(f'Interface {name} not found.')
1236 raise
1237 if not bool_any and setup_state:
1238 for link in links_with_operstate:
1239 self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
1240
1241 def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
1242 for _ in range(timeout_sec * 2):
1243 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1244 if re.search(address_regex, output) and 'tentative' not in output:
1245 break
1246 time.sleep(0.5)
1247
1248 self.assertRegex(output, address_regex)
1249
1250 def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
1251 for _ in range(timeout_sec * 2):
1252 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1253 if not re.search(address_regex, output):
1254 break
1255 time.sleep(0.5)
1256
1257 self.assertNotRegex(output, address_regex)
1258
1259 def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1260 for _ in range(timeout_sec * 2):
1261 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1262 if re.search(route_regex, output):
1263 break
1264 time.sleep(0.5)
1265
1266 self.assertRegex(output, route_regex)
1267
1268 def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1269 for _ in range(timeout_sec * 2):
1270 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1271 if not re.search(route_regex, output):
1272 break
1273 time.sleep(0.5)
1274
1275 self.assertNotRegex(output, route_regex)
1276
1277 def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
1278 if not shutil.which('selinuxenabled'):
1279 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1280 elif call_quiet('selinuxenabled') != 0:
1281 print('## Checking NetLabel skipped: SELinux disabled.')
1282 elif not shutil.which('netlabelctl'): # not packaged by all distros
1283 print('## Checking NetLabel skipped: netlabelctl command not found.')
1284 else:
1285 output = check_output('netlabelctl unlbl list')
1286 print(output)
1287 self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
1288
1289 def setup_nftset(self, filter_name, filter_type, flags=''):
1290 if not shutil.which('nft'):
1291 print('## Setting up NFT sets skipped: nft command not found.')
1292 else:
1293 if call(f'nft add table inet sd_test') != 0:
1294 print('## Setting up NFT table failed.')
1295 self.fail()
1296 if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1297 print('## Setting up NFT sets failed.')
1298 self.fail()
1299
1300 def teardown_nftset(self, *filters):
1301 if not shutil.which('nft'):
1302 print('## Tearing down NFT sets skipped: nft command not found.')
1303 else:
1304 for filter_name in filters:
1305 if call(f'nft delete set inet sd_test {filter_name}') != 0:
1306 print('## Tearing down NFT sets failed.')
1307 self.fail()
1308 if call(f'nft delete table inet sd_test') != 0:
1309 print('## Tearing down NFT table failed.')
1310 self.fail()
1311
1312 def check_nftset(self, filter_name, contents):
1313 if not shutil.which('nft'):
1314 print('## Checking NFT sets skipped: nft command not found.')
1315 else:
1316 output = check_output(f'nft list set inet sd_test {filter_name}')
1317 print(output)
1318 self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
1319
1320 def check_networkd_log(self, contents, since=None, trial=20):
1321 for _ in range(trial):
1322 if contents in read_networkd_log(since=since):
1323 break
1324 time.sleep(0.5)
1325 else:
1326 self.fail(f'"{contents}" not found in journal.')
1327
1328 def networkctl_check_unit(self, ifname, netdev_file=None, network_file=None, link_file=None):
1329 output = networkctl_status(ifname)
1330 print(output)
1331 if netdev_file:
1332 self.assertRegex(output, rf'NetDev File: .*/{netdev_file}\.netdev')
1333 else:
1334 self.assertNotIn('NetDev File:', output)
1335 if network_file:
1336 self.assertRegex(output, rf'Network File: .*/{network_file}\.network')
1337 else:
1338 self.assertIn('Network File: n/a', output)
1339 if link_file:
1340 self.assertRegex(output, rf'Link File: .*/{link_file}\.link')
1341
1342 class NetworkctlTests(unittest.TestCase, Utilities):
1343
1344 def setUp(self):
1345 setup_common()
1346
1347 def tearDown(self):
1348 tear_down_common()
1349
1350 @expectedFailureIfAlternativeNameIsNotAvailable()
1351 def test_altname(self):
1352 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1353 start_networkd()
1354 self.wait_online('dummy98:degraded')
1355
1356 output = networkctl_status('dummy98')
1357 self.assertRegex(output, 'hogehogehogehogehogehoge')
1358
1359 @expectedFailureIfAlternativeNameIsNotAvailable()
1360 def test_rename_to_altname(self):
1361 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1362 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1363 start_networkd()
1364 self.wait_online('dummyalt:degraded')
1365
1366 output = networkctl_status('dummyalt')
1367 self.assertIn('hogehogehogehogehogehoge', output)
1368 self.assertNotIn('dummy98', output)
1369
1370 def test_reconfigure(self):
1371 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
1372 start_networkd()
1373 self.wait_online('dummy98:routable')
1374
1375 output = check_output('ip -4 address show dev dummy98')
1376 print(output)
1377 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1378 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1379 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1380
1381 check_output('ip address del 10.1.2.3/16 dev dummy98')
1382 check_output('ip address del 10.1.2.4/16 dev dummy98')
1383 check_output('ip address del 10.2.2.4/16 dev dummy98')
1384
1385 networkctl_reconfigure('dummy98')
1386 self.wait_online('dummy98:routable')
1387
1388 output = check_output('ip -4 address show dev dummy98')
1389 print(output)
1390 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1391 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1392 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1393
1394 remove_network_unit('25-address-static.network')
1395
1396 networkctl_reload()
1397 self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
1398
1399 output = check_output('ip -4 address show dev dummy98')
1400 print(output)
1401 self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1402 self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1403 self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1404
1405 copy_network_unit('25-address-static.network', copy_dropins=False)
1406 networkctl_reload()
1407 self.wait_online('dummy98:routable')
1408
1409 output = check_output('ip -4 address show dev dummy98')
1410 print(output)
1411 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1412 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1413 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1414
1415 def test_renew(self):
1416 def check():
1417 self.wait_online('veth99:routable', 'veth-peer:routable')
1418 output = networkctl_status('veth99')
1419 print(output)
1420 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
1421 self.assertIn('Gateway: 192.168.5.3', output)
1422 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
1423 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
1424
1425 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1426 start_networkd()
1427 check()
1428 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1429
1430 for verb in ['renew', 'forcerenew']:
1431 networkctl(verb, 'veth99')
1432 check()
1433 networkctl(verb, 'veth99', 'veth99', 'veth99')
1434 check()
1435
1436 def test_up_down(self):
1437 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
1438 start_networkd()
1439 self.wait_online('dummy98:routable')
1440
1441 networkctl('down', 'dummy98')
1442 self.wait_online('dummy98:off')
1443 networkctl('up', 'dummy98')
1444 self.wait_online('dummy98:routable')
1445 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1446 self.wait_online('dummy98:off')
1447 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1448 self.wait_online('dummy98:routable')
1449
1450 def test_reload(self):
1451 start_networkd()
1452
1453 copy_network_unit('11-dummy.netdev')
1454 networkctl_reload()
1455 self.wait_operstate('test1', 'off', setup_state='unmanaged')
1456
1457 copy_network_unit('11-dummy.network')
1458 networkctl_reload()
1459 self.wait_online('test1:degraded')
1460
1461 remove_network_unit('11-dummy.network')
1462 networkctl_reload()
1463 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
1464
1465 remove_network_unit('11-dummy.netdev')
1466 networkctl_reload()
1467 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
1468
1469 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1470 networkctl_reload()
1471 self.wait_operstate('test1', 'degraded')
1472
1473 def test_glob(self):
1474 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1475 start_networkd()
1476
1477 self.wait_online('test1:degraded')
1478
1479 output = networkctl('list')
1480 self.assertRegex(output, '1 lo ')
1481 self.assertRegex(output, 'test1')
1482
1483 output = networkctl('list', 'test1')
1484 self.assertNotRegex(output, '1 lo ')
1485 self.assertRegex(output, 'test1')
1486
1487 output = networkctl('list', 'te*')
1488 self.assertNotRegex(output, '1 lo ')
1489 self.assertRegex(output, 'test1')
1490
1491 output = networkctl_status('te*')
1492 self.assertNotRegex(output, '1: lo ')
1493 self.assertRegex(output, 'test1')
1494
1495 output = networkctl_status('tes[a-z][0-9]')
1496 self.assertNotRegex(output, '1: lo ')
1497 self.assertRegex(output, 'test1')
1498
1499 def test_mtu(self):
1500 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1501 start_networkd()
1502
1503 self.wait_online('test1:degraded')
1504
1505 output = networkctl_status('test1')
1506 self.assertRegex(output, 'MTU: 1600')
1507
1508 def test_type(self):
1509 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1510 start_networkd()
1511 self.wait_online('test1:degraded')
1512
1513 output = networkctl_status('test1')
1514 print(output)
1515 self.assertRegex(output, 'Type: ether')
1516
1517 output = networkctl_status('lo')
1518 print(output)
1519 self.assertRegex(output, 'Type: loopback')
1520
1521 def test_unit_file(self):
1522 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1523 start_networkd()
1524 self.wait_online('test1:degraded')
1525
1526 output = networkctl_status('test1')
1527 print(output)
1528 self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
1529 self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
1530 self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
1531 self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
1532
1533 self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).')
1534
1535 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1536 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1537 # Let's reprocess the interface and drop the property.
1538 udevadm_trigger('/sys/class/net/lo')
1539 output = networkctl_status('lo')
1540 print(output)
1541 self.assertIn('Link File: n/a', output)
1542 self.assertIn('Network File: n/a', output)
1543
1544 def test_delete_links(self):
1545 copy_network_unit('11-dummy.netdev', '25-veth.netdev')
1546 start_networkd()
1547 self.wait_links('test1', 'veth99', 'veth-peer')
1548 networkctl('delete', 'test1', 'veth99')
1549 self.check_link_exists('test1', 'veth99', 'veth-peer', expected=False)
1550
1551 def test_label(self):
1552 networkctl('label')
1553
1554 class NetworkdMatchTests(unittest.TestCase, Utilities):
1555
1556 def setUp(self):
1557 setup_common()
1558
1559 def tearDown(self):
1560 tear_down_common()
1561
1562 @expectedFailureIfAlternativeNameIsNotAvailable()
1563 def test_match(self):
1564 copy_network_unit('12-dummy-mac.netdev',
1565 '12-dummy-match-mac-01.network',
1566 '12-dummy-match-mac-02.network',
1567 '12-dummy-match-renamed.network',
1568 '12-dummy-match-altname.network',
1569 '12-dummy-altname.link')
1570 start_networkd()
1571
1572 self.wait_online('dummy98:routable')
1573 output = networkctl_status('dummy98')
1574 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
1575 output = check_output('ip -4 address show dev dummy98')
1576 self.assertIn('10.0.0.1/16', output)
1577
1578 check_output('ip link set dev dummy98 down')
1579 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1580
1581 self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
1582 self.wait_online('dummy98:routable')
1583 output = networkctl_status('dummy98')
1584 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
1585
1586 check_output('ip link set dev dummy98 down')
1587 check_output('ip link set dev dummy98 name dummy98-1')
1588
1589 self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
1590 self.wait_online('dummy98-1:routable')
1591 output = networkctl_status('dummy98-1')
1592 self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
1593
1594 check_output('ip link set dev dummy98-1 down')
1595 check_output('ip link set dev dummy98-1 name dummy98-2')
1596 udevadm_trigger('/sys/class/net/dummy98-2')
1597
1598 self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
1599 self.wait_online('dummy98-2:routable')
1600 output = networkctl_status('dummy98-2')
1601 self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
1602
1603 def test_match_udev_property(self):
1604 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1605 start_networkd()
1606 self.wait_online('dummy98:routable')
1607
1608 output = networkctl_status('dummy98')
1609 print(output)
1610 self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
1611
1612 class WaitOnlineTests(unittest.TestCase, Utilities):
1613
1614 def setUp(self):
1615 setup_common()
1616
1617 def tearDown(self):
1618 tear_down_common()
1619
1620 def test_wait_online_any(self):
1621 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1622 start_networkd()
1623
1624 self.wait_online('bridge99', 'test1:degraded', bool_any=True)
1625
1626 self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
1627 self.wait_operstate('test1', 'degraded')
1628
1629 def do_test_wait_online_dns(
1630 self,
1631 global_dns='',
1632 fallback_dns='',
1633 expect_timeout=False,
1634 network_dropin=None,
1635 ):
1636 global wait_online_env
1637
1638 if network_dropin is not None:
1639 network_dropin_path = os.path.join(
1640 network_unit_dir,
1641 '25-dhcp-client-use-dns-ipv4.network.d/test.conf'
1642 )
1643 mkdir_p(os.path.dirname(network_dropin_path))
1644 with open(network_dropin_path, 'w') as f:
1645 f.write(network_dropin)
1646
1647 copy_network_unit(
1648 '25-veth.netdev',
1649 '25-dhcp-client-use-dns-ipv4.network',
1650 '25-dhcp-server.network'
1651 )
1652 start_networkd()
1653 self.wait_online('veth-peer:routable')
1654
1655 # Unless given, clear global DNS configuration
1656 resolved_dropin = '/run/systemd/resolved.conf.d/global-dns.conf'
1657 mkdir_p(os.path.dirname(resolved_dropin))
1658 with open(resolved_dropin, 'w') as f:
1659 f.write((
1660 '[Resolve]\n'
1661 f'DNS={global_dns}\n'
1662 f'FallbackDNS={fallback_dns}\n'
1663 ))
1664 self.addCleanup(os.remove, resolved_dropin)
1665 check_output('systemctl reload systemd-resolved')
1666
1667 try:
1668 wait_online_env_copy = wait_online_env.copy()
1669
1670 wait_online_env['SYSTEMD_LOG_LEVEL'] = 'debug'
1671 wait_online_env['SYSTEMD_LOG_TARGET'] = 'console'
1672
1673 self.wait_online('veth99:routable', bool_dns=True)
1674
1675 if expect_timeout:
1676 # The above should have thrown an exception.
1677 self.fail(
1678 'Expected systemd-networkd-wait-online to time out'
1679 )
1680
1681 except subprocess.CalledProcessError as e:
1682 if expect_timeout:
1683 self.assertRegex(
1684 e.output,
1685 f'veth99: No link-specific DNS server is accessible',
1686 f'Missing expected log message:\n{e.output}'
1687 )
1688 else:
1689 self.fail(
1690 f'Command timed out:\n{e.output}'
1691 )
1692 finally:
1693 wait_online_env = wait_online_env_copy
1694
1695 def test_wait_online_dns(self):
1696 ''' test systemd-networkd-wait-online with --dns '''
1697 self.do_test_wait_online_dns()
1698
1699 def test_wait_online_dns_global(self):
1700 '''
1701 test systemd-networkd-wait-online with --dns, expect pass due to global DNS
1702 '''
1703
1704 # Set UseDNS=no, and allow global DNS to be used.
1705 self.do_test_wait_online_dns(
1706 global_dns='192.168.5.1',
1707 network_dropin=(
1708 '[DHCPv4]\n'
1709 'UseDNS=no\n'
1710 )
1711 )
1712
1713 def test_wait_online_dns_expect_timeout(self):
1714 ''' test systemd-networkd-wait-online with --dns, and expect timeout '''
1715
1716 # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
1717 self.do_test_wait_online_dns(
1718 expect_timeout=True,
1719 network_dropin=(
1720 '[Network]\n'
1721 'DNSDefaultRoute=yes\n'
1722 '[DHCPv4]\n'
1723 'UseDNS=no\n'
1724 )
1725 )
1726
1727 def test_wait_online_dns_expect_timeout_global(self):
1728 '''
1729 test systemd-networkd-wait-online with --dns, and expect timeout
1730 despite global DNS
1731 '''
1732
1733 # Configure Domains=~., and expect timeout despite global DNS servers
1734 # being available.
1735 self.do_test_wait_online_dns(
1736 expect_timeout=True,
1737 global_dns='192.168.5.1',
1738 network_dropin=(
1739 '[Network]\n'
1740 'Domains=~.\n'
1741 '[DHCPv4]\n'
1742 'UseDNS=no\n'
1743 )
1744 )
1745
1746
1747 class NetworkdNetDevTests(unittest.TestCase, Utilities):
1748
1749 def setUp(self):
1750 setup_common()
1751
1752 def tearDown(self):
1753 tear_down_common()
1754
1755 def test_dropin_and_name_conflict(self):
1756 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1757 start_networkd()
1758
1759 self.wait_online('dropin-test:off', setup_state='unmanaged')
1760
1761 output = check_output('ip link show dropin-test')
1762 print(output)
1763 self.assertRegex(output, '00:50:56:c0:00:28')
1764
1765 @expectedFailureIfModuleIsNotAvailable('bareudp')
1766 def test_bareudp(self):
1767 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1768 start_networkd()
1769
1770 self.wait_online('bareudp99:degraded')
1771 self.networkctl_check_unit('bareudp99', '25-bareudp', '26-netdev-link-local-addressing-yes')
1772
1773 output = check_output('ip -d link show bareudp99')
1774 print(output)
1775 self.assertRegex(output, 'dstport 1000 ')
1776 self.assertRegex(output, 'ethertype ip ')
1777 self.assertRegex(output, 'srcportmin 1001 ')
1778
1779 touch_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1780 networkctl_reload()
1781 self.wait_online('bareudp99:degraded')
1782
1783 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1784 def test_batadv(self):
1785 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1786 start_networkd()
1787
1788 self.wait_online('batadv99:degraded')
1789 self.networkctl_check_unit('batadv99', '25-batadv', '26-netdev-link-local-addressing-yes')
1790
1791 output = check_output('ip -d link show batadv99')
1792 print(output)
1793 self.assertRegex(output, 'batadv')
1794
1795 touch_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1796 networkctl_reload()
1797 self.wait_online('batadv99:degraded')
1798
1799 def test_bridge(self):
1800 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1801 start_networkd()
1802
1803 self.wait_online('bridge99:no-carrier')
1804 self.networkctl_check_unit('bridge99', '25-bridge', '25-bridge-configure-without-carrier')
1805
1806 tick = os.sysconf('SC_CLK_TCK')
1807 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
1808 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
1809 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick))
1810 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick))
1811 self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1812 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1813 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1814 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1815 self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1816 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn')))
1817
1818 output = networkctl_status('bridge99')
1819 print(output)
1820 self.assertRegex(output, 'Priority: 9')
1821 self.assertRegex(output, 'STP: yes')
1822 self.assertRegex(output, 'Multicast IGMP Version: 3')
1823 if 'FDB Max Learned' in output:
1824 self.assertRegex(output, 'FDB Max Learned: 4')
1825
1826 output = check_output('ip -d link show bridge99')
1827 print(output)
1828 self.assertIn('vlan_filtering 1 ', output)
1829 self.assertIn('vlan_protocol 802.1ad ', output)
1830 self.assertIn('vlan_default_pvid 9 ', output)
1831 if 'fdb_max_learned' in output:
1832 self.assertIn('fdb_max_learned 4 ', output)
1833
1834 def test_bond(self):
1835 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
1836 start_networkd()
1837
1838 self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged')
1839 self.networkctl_check_unit('bond99', '25-bond')
1840 self.networkctl_check_unit('bond98', '25-bond-balanced-tlb')
1841 self.networkctl_check_unit('bond97', '25-bond-property')
1842
1843 self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1844 self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1845 self.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1846 self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1847 self.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1848 self.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1849 self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1850 self.check_link_attr('bond99', 'bonding', 'min_links', '1')
1851 self.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1852 self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1853 self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1854
1855 self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1856 self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1857
1858 output = networkctl_status('bond99')
1859 print(output)
1860 self.assertIn('Mode: 802.3ad', output)
1861 self.assertIn('Miimon: 1s', output)
1862 self.assertIn('Updelay: 2s', output)
1863 self.assertIn('Downdelay: 2s', output)
1864
1865 output = networkctl_status('bond98')
1866 print(output)
1867 self.assertIn('Mode: balance-tlb', output)
1868
1869 output = networkctl_status('bond97')
1870 print(output)
1871
1872 self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
1873 self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
1874
1875 def check_vlan(self, id, flags):
1876 self.wait_online('test1:degraded', 'vlan99:routable')
1877 self.networkctl_check_unit('vlan99', '21-vlan', '21-vlan')
1878 self.networkctl_check_unit('test1', '11-dummy', '21-vlan-test1')
1879
1880 output = check_output('ip -d link show test1')
1881 print(output)
1882 self.assertRegex(output, ' mtu 2000 ')
1883
1884 output = check_output('ip -d link show vlan99')
1885 print(output)
1886 self.assertIn(' mtu 2000 ', output)
1887 if flags:
1888 self.assertIn('REORDER_HDR', output)
1889 self.assertIn('LOOSE_BINDING', output)
1890 self.assertIn('GVRP', output)
1891 self.assertIn('MVRP', output)
1892 else:
1893 self.assertNotIn('REORDER_HDR', output)
1894 self.assertNotIn('LOOSE_BINDING', output)
1895 self.assertNotIn('GVRP', output)
1896 self.assertNotIn('MVRP', output)
1897 self.assertIn(f' id {id} ', output)
1898 self.assertIn('ingress-qos-map { 4:100 7:13 }', output)
1899 self.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output)
1900
1901 output = check_output('ip -4 address show dev test1')
1902 print(output)
1903 self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1904 self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1905
1906 output = check_output('ip -4 address show dev vlan99')
1907 print(output)
1908 self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1909
1910 def test_vlan(self):
1911 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1912 '21-vlan.network', '21-vlan-test1.network')
1913 start_networkd()
1914 self.check_vlan(id=99, flags=True)
1915
1916 # Test for reloading .netdev file. See issue #34907.
1917 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1918 f.write('[VLAN]\nId=42\n')
1919
1920 # VLAN ID cannot be changed, so we need to remove the existing netdev.
1921 check_output("ip link del vlan99")
1922 networkctl_reload()
1923 self.check_vlan(id=42, flags=True)
1924
1925 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1926 f.write('[VLAN]\n'
1927 'GVRP=no\n'
1928 'MVRP=no\n'
1929 'LooseBinding=no\n'
1930 'ReorderHeader=no\n')
1931
1932 # flags can be changed, hence it is not necessary to remove the existing netdev.
1933 networkctl_reload()
1934 self.check_vlan(id=42, flags=False)
1935
1936 def test_vlan_on_bond(self):
1937 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1938 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1939
1940 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1941 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1942 start_networkd()
1943 self.wait_online('bond99:off')
1944 self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
1945 self.networkctl_check_unit('vlan99', '21-vlan-on-bond', '21-vlan-on-bond')
1946 self.networkctl_check_unit('bond99', '21-bond-802.3ad', '21-bond-802.3ad')
1947
1948 self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
1949
1950 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1951 networkctl_reload()
1952 self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1953
1954 def test_macvtap(self):
1955 first = True
1956 for mode in ['private', 'vepa', 'bridge', 'passthru']:
1957 if first:
1958 first = False
1959 else:
1960 self.tearDown()
1961
1962 print(f'### test_macvtap(mode={mode})')
1963 with self.subTest(mode=mode):
1964 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1965 '11-dummy.netdev', '25-macvtap.network')
1966 with open(os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8') as f:
1967 f.write('[MACVTAP]\nMode=' + mode)
1968 start_networkd()
1969
1970 self.wait_online('macvtap99:degraded',
1971 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1972 self.networkctl_check_unit('macvtap99', '21-macvtap', '26-netdev-link-local-addressing-yes')
1973 self.networkctl_check_unit('test1', '11-dummy', '25-macvtap')
1974
1975 output = check_output('ip -d link show macvtap99')
1976 print(output)
1977 self.assertRegex(output, 'macvtap mode ' + mode + ' ')
1978
1979 touch_network_unit('21-macvtap.netdev')
1980 networkctl_reload()
1981 self.wait_online('macvtap99:degraded',
1982 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1983
1984 @expectedFailureIfModuleIsNotAvailable('macvlan')
1985 def test_macvlan(self):
1986 first = True
1987 for mode in ['private', 'vepa', 'bridge', 'passthru']:
1988 if first:
1989 first = False
1990 else:
1991 self.tearDown()
1992
1993 print(f'### test_macvlan(mode={mode})')
1994 with self.subTest(mode=mode):
1995 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1996 '11-dummy.netdev', '25-macvlan.network')
1997 with open(os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8') as f:
1998 f.write('[MACVLAN]\nMode=' + mode)
1999 start_networkd()
2000
2001 self.wait_online('macvlan99:degraded',
2002 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2003 self.networkctl_check_unit('macvlan99', '21-macvlan', '26-netdev-link-local-addressing-yes')
2004 self.networkctl_check_unit('test1', '11-dummy', '25-macvlan')
2005
2006 output = check_output('ip -d link show test1')
2007 print(output)
2008 self.assertIn(' mtu 2000 ', output)
2009
2010 output = check_output('ip -d link show macvlan99')
2011 print(output)
2012 self.assertIn(' mtu 2000 ', output)
2013 self.assertIn(f' macvlan mode {mode} ', output)
2014
2015 remove_link('test1')
2016 time.sleep(1)
2017
2018 check_output("ip link add test1 type dummy")
2019 self.wait_online('macvlan99:degraded',
2020 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2021
2022 output = check_output('ip -d link show test1')
2023 print(output)
2024 self.assertIn(' mtu 2000 ', output)
2025
2026 output = check_output('ip -d link show macvlan99')
2027 print(output)
2028 self.assertIn(' mtu 2000 ', output)
2029 self.assertIn(f' macvlan mode {mode} ', output)
2030 self.assertIn(' bcqueuelen 1234 ', output)
2031 if ' bclim ' in output: # This is new in kernel and iproute2 v6.4
2032 self.assertIn(' bclim 2147483647 ', output)
2033
2034 touch_network_unit('21-macvlan.netdev')
2035 networkctl_reload()
2036 self.wait_online('macvlan99:degraded',
2037 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2038
2039 @expectedFailureIfModuleIsNotAvailable('ipvlan')
2040 def test_ipvlan(self):
2041 first = True
2042 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
2043 if first:
2044 first = False
2045 else:
2046 self.tearDown()
2047
2048 print(f'### test_ipvlan(mode={mode}, flag={flag})')
2049 with self.subTest(mode=mode, flag=flag):
2050 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
2051 '11-dummy.netdev', '25-ipvlan.network')
2052 with open(os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8') as f:
2053 f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
2054
2055 start_networkd()
2056 self.wait_online('ipvlan99:degraded', 'test1:degraded')
2057 self.networkctl_check_unit('ipvlan99', '25-ipvlan', '26-netdev-link-local-addressing-yes')
2058 self.networkctl_check_unit('test1', '11-dummy', '25-ipvlan')
2059
2060 output = check_output('ip -d link show ipvlan99')
2061 print(output)
2062 self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
2063
2064 touch_network_unit('25-ipvlan.netdev')
2065 networkctl_reload()
2066 self.wait_online('ipvlan99:degraded', 'test1:degraded')
2067
2068 @expectedFailureIfModuleIsNotAvailable('hsr')
2069 def test_hsr(self):
2070 first = True
2071 for proto, supervision in [['hsr', 9], ['prp', 127]]:
2072 if first:
2073 first = False
2074 else:
2075 self.tearDown()
2076
2077 print(f'### test_hsr(proto={proto}, supervision={supervision})')
2078 with self.subTest(proto=proto, supervision=supervision):
2079 copy_network_unit('25-hsr.netdev', '25-hsr.network',
2080 '11-dummy.netdev', '11-dummy.network',
2081 '12-dummy.netdev', '12-dummy-no-address.network')
2082 with open(os.path.join(network_unit_dir, '25-hsr.netdev'), mode='a', encoding='utf-8') as f:
2083 f.write('Protocol=' + proto + '\nSupervision=' + str(supervision))
2084
2085 start_networkd()
2086 self.wait_online('hsr99:degraded')
2087 self.networkctl_check_unit('hsr99', '25-hsr', '25-hsr')
2088 self.networkctl_check_unit('test1', '11-dummy', '11-dummy')
2089 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy-no-address')
2090
2091 output = check_output('ip -d link show hsr99')
2092 print(output)
2093 self.assertRegex(output, 'hsr slave1 test1 slave2 dummy98')
2094 self.assertRegex(output, f'supervision 01:15:4e:00:01:{supervision:02x}')
2095 self.assertRegex(output, 'proto ' + ('0' if proto == 'hsr' else '1') + ' ')
2096
2097 touch_network_unit('25-hsr.netdev')
2098 networkctl_reload()
2099 self.wait_online('hsr99:degraded')
2100
2101 @expectedFailureIfModuleIsNotAvailable('ipvtap')
2102 def test_ipvtap(self):
2103 first = True
2104 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
2105 if first:
2106 first = False
2107 else:
2108 self.tearDown()
2109
2110 print(f'### test_ipvtap(mode={mode}, flag={flag})')
2111 with self.subTest(mode=mode, flag=flag):
2112 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
2113 '11-dummy.netdev', '25-ipvtap.network')
2114 with open(os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8') as f:
2115 f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
2116
2117 start_networkd()
2118 self.wait_online('ipvtap99:degraded', 'test1:degraded')
2119 self.networkctl_check_unit('ipvtap99', '25-ipvtap', '26-netdev-link-local-addressing-yes')
2120 self.networkctl_check_unit('test1', '11-dummy', '25-ipvtap')
2121
2122 output = check_output('ip -d link show ipvtap99')
2123 print(output)
2124 self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
2125
2126 touch_network_unit('25-ipvtap.netdev')
2127 networkctl_reload()
2128 self.wait_online('ipvtap99:degraded', 'test1:degraded')
2129
2130 def test_veth(self):
2131 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
2132 '25-veth-mtu.netdev')
2133 start_networkd()
2134
2135 self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
2136 self.networkctl_check_unit('veth99', '25-veth', '26-netdev-link-local-addressing-yes')
2137 self.networkctl_check_unit('veth-peer', '25-veth', '26-netdev-link-local-addressing-yes')
2138 self.networkctl_check_unit('veth-mtu', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
2139 self.networkctl_check_unit('veth-mtu-peer', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
2140
2141 output = check_output('ip -d link show veth99')
2142 print(output)
2143 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
2144 output = check_output('ip -d link show veth-peer')
2145 print(output)
2146 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
2147
2148 output = check_output('ip -d link show veth-mtu')
2149 print(output)
2150 self.assertRegex(output, 'link/ether 12:34:56:78:9a:be')
2151 self.assertRegex(output, 'mtu 1800')
2152 output = check_output('ip -d link show veth-mtu-peer')
2153 print(output)
2154 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
2155 self.assertRegex(output, 'mtu 1800')
2156
2157 touch_network_unit(
2158 '25-veth.netdev',
2159 '26-netdev-link-local-addressing-yes.network',
2160 '25-veth-mtu.netdev')
2161 networkctl_reload()
2162 self.wait_online(
2163 'veth99:degraded',
2164 'veth-peer:degraded',
2165 'veth-mtu:degraded',
2166 'veth-mtu-peer:degraded')
2167
2168 def check_tuntap(self, attached):
2169 pid = networkd_pid()
2170 name = psutil.Process(pid).name()[:15]
2171
2172 output = check_output('ip -d -oneline tuntap show')
2173 print(output)
2174 self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
2175 self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
2176
2177 if attached:
2178 self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)')
2179 self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)')
2180 self.assertRegex(output, r'testtap99: .*systemd\(1\)')
2181 self.assertRegex(output, r'testtun99: .*systemd\(1\)')
2182
2183 output = check_output('ip -d link show testtun99')
2184 print(output)
2185 # Old ip command does not support IFF_ flags
2186 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2187 self.assertIn('UP,LOWER_UP', output)
2188
2189 output = check_output('ip -d link show testtap99')
2190 print(output)
2191 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2192 self.assertIn('UP,LOWER_UP', output)
2193
2194 else:
2195 self.assertNotIn(f'{name}({pid})', output)
2196 self.assertNotIn('systemd(1)', output)
2197
2198 for _ in range(20):
2199 output = check_output('ip -d link show testtun99')
2200 print(output)
2201 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2202 if 'NO-CARRIER' in output:
2203 break
2204 time.sleep(0.5)
2205 else:
2206 self.fail()
2207
2208 for _ in range(20):
2209 output = check_output('ip -d link show testtap99')
2210 print(output)
2211 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2212 if 'NO-CARRIER' in output:
2213 break
2214 time.sleep(0.5)
2215 else:
2216 self.fail()
2217
2218 def test_tuntap(self):
2219 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2220 start_networkd()
2221 self.wait_online('testtun99:degraded', 'testtap99:degraded')
2222 self.networkctl_check_unit('testtap99', '25-tap', '26-netdev-link-local-addressing-yes')
2223 self.networkctl_check_unit('testtun99', '25-tun', '26-netdev-link-local-addressing-yes')
2224
2225 self.check_tuntap(True)
2226
2227 touch_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2228 networkctl_reload()
2229 self.wait_online('testtun99:degraded', 'testtap99:degraded')
2230
2231 self.check_tuntap(True)
2232
2233 remove_network_unit('26-netdev-link-local-addressing-yes.network')
2234 restart_networkd()
2235 self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged')
2236 self.networkctl_check_unit('testtap99', '25-tap')
2237 self.networkctl_check_unit('testtun99', '25-tun')
2238
2239 self.check_tuntap(True)
2240
2241 clear_network_units()
2242 unmanage_existing_links()
2243 restart_networkd()
2244 self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged')
2245 self.networkctl_check_unit('testtap99')
2246 self.networkctl_check_unit('testtun99')
2247
2248 self.check_tuntap(False)
2249
2250 @expectedFailureIfModuleIsNotAvailable('vrf')
2251 def test_vrf(self):
2252 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2253 start_networkd()
2254
2255 self.wait_online('vrf99:carrier')
2256 self.networkctl_check_unit('vrf99', '25-vrf', '26-netdev-link-local-addressing-yes')
2257
2258 touch_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2259 networkctl()
2260 self.wait_online('vrf99:carrier')
2261
2262 @expectedFailureIfModuleIsNotAvailable('vcan')
2263 def test_vcan(self):
2264 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
2265 '25-vcan98.netdev', '25-vcan98.network')
2266 start_networkd()
2267
2268 self.wait_online('vcan99:carrier', 'vcan98:carrier')
2269 # For can devices, 'carrier' is the default required operational state.
2270 self.wait_online('vcan99', 'vcan98')
2271 self.networkctl_check_unit('vcan99', '25-vcan', '26-netdev-link-local-addressing-yes')
2272 self.networkctl_check_unit('vcan98', '25-vcan98', '25-vcan98')
2273
2274 # https://github.com/systemd/systemd/issues/30140
2275 output = check_output('ip -d link show vcan99')
2276 print(output)
2277 self.assertIn('mtu 16 ', output)
2278
2279 output = check_output('ip -d link show vcan98')
2280 print(output)
2281 self.assertIn('mtu 16 ', output)
2282
2283 touch_network_unit(
2284 '25-vcan.netdev',
2285 '26-netdev-link-local-addressing-yes.network',
2286 '25-vcan98.netdev',
2287 '25-vcan98.network')
2288 networkctl_reload()
2289 self.wait_online('vcan99:carrier', 'vcan98:carrier')
2290
2291 @expectedFailureIfModuleIsNotAvailable('vxcan')
2292 def test_vxcan(self):
2293 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2294 start_networkd()
2295
2296 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
2297 # For can devices, 'carrier' is the default required operational state.
2298 self.wait_online('vxcan99', 'vxcan-peer')
2299 self.networkctl_check_unit('vxcan99', '25-vxcan', '26-netdev-link-local-addressing-yes')
2300 self.networkctl_check_unit('vxcan-peer', '25-vxcan', '26-netdev-link-local-addressing-yes')
2301
2302 touch_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2303 networkctl()
2304 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
2305
2306 @expectedFailureIfModuleIsNotAvailable('wireguard')
2307 def test_wireguard(self):
2308 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
2309 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
2310 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
2311
2312 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
2313 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
2314 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
2315 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2316 start_networkd()
2317 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
2318 self.networkctl_check_unit('wg99', '25-wireguard', '25-wireguard')
2319 self.networkctl_check_unit('wg98', '25-wireguard-23-peers', '25-wireguard-23-peers')
2320 self.networkctl_check_unit('wg97', '25-wireguard-no-peer', '25-wireguard-no-peer')
2321
2322 output = check_output('ip -4 address show dev wg99')
2323 print(output)
2324 self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
2325
2326 output = check_output('ip -4 address show dev wg99')
2327 print(output)
2328 self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
2329
2330 output = check_output('ip -6 address show dev wg99')
2331 print(output)
2332 self.assertIn('inet6 fe80::1/64 scope link', output)
2333
2334 output = check_output('ip -4 address show dev wg98')
2335 print(output)
2336 self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
2337
2338 output = check_output('ip -6 address show dev wg98')
2339 print(output)
2340 self.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output)
2341
2342 output = check_output('ip -4 route show dev wg99 table 1234')
2343 print(output)
2344 self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
2345
2346 output = check_output('ip -6 route show dev wg99 table 1234')
2347 print(output)
2348 self.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output)
2349
2350 output = check_output('ip -6 route show dev wg98 table 1234')
2351 print(output)
2352 self.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output)
2353 self.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output)
2354 self.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output)
2355 self.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output)
2356 self.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output)
2357 self.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output)
2358 self.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output)
2359 self.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output)
2360 self.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output)
2361 self.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output)
2362 self.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output)
2363 self.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output)
2364 self.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output)
2365 self.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output)
2366 self.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output)
2367 self.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output)
2368 self.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output)
2369 self.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output)
2370 self.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output)
2371 self.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output)
2372 self.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output)
2373 self.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output)
2374 self.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output)
2375 self.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output)
2376 self.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output)
2377 self.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output)
2378 self.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output)
2379 self.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output)
2380 self.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output)
2381 self.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output)
2382 self.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output)
2383 self.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output)
2384 self.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output)
2385 self.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output)
2386 self.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output)
2387 self.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output)
2388 self.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output)
2389 self.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output)
2390 self.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output)
2391 self.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output)
2392 self.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output)
2393 self.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output)
2394 self.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output)
2395 self.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output)
2396 self.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output)
2397 self.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output)
2398
2399 if shutil.which('wg'):
2400 call('wg')
2401
2402 output = check_output('wg show wg99 listen-port')
2403 self.assertEqual(output, '51820')
2404 output = check_output('wg show wg99 fwmark')
2405 self.assertEqual(output, '0x4d2')
2406 output = check_output('wg show wg99 private-key')
2407 self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
2408 output = check_output('wg show wg99 allowed-ips')
2409 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
2410 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
2411 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
2412 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
2413 output = check_output('wg show wg99 persistent-keepalive')
2414 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
2415 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
2416 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
2417 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
2418 output = check_output('wg show wg99 endpoints')
2419 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
2420 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
2421 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
2422 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
2423 output = check_output('wg show wg99 preshared-keys')
2424 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
2425 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
2426 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
2427 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
2428
2429 output = check_output('wg show wg98 private-key')
2430 self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
2431
2432 output = check_output('wg show wg97 listen-port')
2433 self.assertEqual(output, '51821')
2434 output = check_output('wg show wg97 fwmark')
2435 self.assertEqual(output, '0x4d3')
2436
2437 touch_network_unit(
2438 '25-wireguard.netdev', '25-wireguard.network',
2439 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
2440 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
2441 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2442 networkctl_reload()
2443 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
2444
2445 def test_geneve(self):
2446 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2447 start_networkd()
2448
2449 self.wait_online('geneve99:degraded')
2450 self.networkctl_check_unit('geneve99', '25-geneve', '26-netdev-link-local-addressing-yes')
2451
2452 output = check_output('ip -d link show geneve99')
2453 print(output)
2454 self.assertRegex(output, '192.168.22.1')
2455 self.assertRegex(output, '6082')
2456 self.assertRegex(output, 'udpcsum')
2457 self.assertRegex(output, 'udp6zerocsumrx')
2458
2459 touch_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2460 networkctl_reload()
2461 self.wait_online('geneve99:degraded')
2462
2463 def _test_ipip_tunnel(self, mode):
2464 copy_network_unit('12-dummy.netdev', '25-ipip.network',
2465 '25-ipip-tunnel.netdev', '25-tunnel.network',
2466 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2467 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2468 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2469
2470 if mode:
2471 for netdev in ['25-ipip-tunnel.netdev',
2472 '25-ipip-tunnel-local-any.netdev',
2473 '25-ipip-tunnel-remote-any.netdev',
2474 '25-ipip-tunnel-any-any.netdev']:
2475 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2476 f.write(f'[Tunnel]\nMode={mode}\n')
2477 else:
2478 mode = 'ipip' # kernel default
2479
2480 start_networkd()
2481 self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
2482
2483 output = check_output('ip -d link show ipiptun99')
2484 print(output)
2485 self.assertIn(f'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98', output)
2486 output = check_output('ip -d link show ipiptun98')
2487 print(output)
2488 self.assertIn(f'ipip {mode} remote 192.169.224.239 local any dev dummy98', output)
2489 output = check_output('ip -d link show ipiptun97')
2490 print(output)
2491 self.assertIn(f'ipip {mode} remote any local 192.168.223.238 dev dummy98', output)
2492 output = check_output('ip -d link show ipiptun96')
2493 print(output)
2494 self.assertIn(f'ipip {mode} remote any local any dev dummy98', output)
2495
2496 touch_network_unit(
2497 '25-ipip-tunnel.netdev',
2498 '25-ipip-tunnel-local-any.netdev',
2499 '25-ipip-tunnel-remote-any.netdev',
2500 '25-ipip-tunnel-any-any.netdev')
2501 networkctl_reload()
2502 self.wait_online(
2503 'ipiptun99:routable',
2504 'ipiptun98:routable',
2505 'ipiptun97:routable',
2506 'ipiptun96:routable',
2507 'dummy98:degraded')
2508
2509 def test_ipip_tunnel(self):
2510 first = True
2511 for mode in [None, 'ipip', 'any']:
2512 if first:
2513 first = False
2514 else:
2515 self.tearDown()
2516
2517 print(f'### test_ipip_tunnel(mode={mode})')
2518 with self.subTest(mode=mode):
2519 self._test_ipip_tunnel(mode)
2520
2521 def test_gre_tunnel(self):
2522 copy_network_unit('12-dummy.netdev', '25-gretun.network',
2523 '25-gre-tunnel.netdev', '25-tunnel.network',
2524 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2525 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2526 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2527 start_networkd()
2528 self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2529 self.networkctl_check_unit('gretun99', '25-gre-tunnel', '25-tunnel')
2530 self.networkctl_check_unit('gretun98', '25-gre-tunnel-local-any', '25-tunnel-local-any')
2531 self.networkctl_check_unit('gretun97', '25-gre-tunnel-remote-any', '25-tunnel-remote-any')
2532 self.networkctl_check_unit('gretun96', '25-gre-tunnel-any-any', '25-tunnel-any-any')
2533 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretun')
2534
2535 output = check_output('ip -d link show gretun99')
2536 print(output)
2537 self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2538 self.assertRegex(output, 'ikey 1.2.3.103')
2539 self.assertRegex(output, 'okey 1.2.4.103')
2540 self.assertRegex(output, 'iseq')
2541 self.assertRegex(output, 'oseq')
2542 output = check_output('ip -d link show gretun98')
2543 print(output)
2544 self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
2545 self.assertRegex(output, 'ikey 0.0.0.104')
2546 self.assertRegex(output, 'okey 0.0.0.104')
2547 self.assertNotRegex(output, 'iseq')
2548 self.assertNotRegex(output, 'oseq')
2549 output = check_output('ip -d link show gretun97')
2550 print(output)
2551 self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
2552 self.assertRegex(output, 'ikey 0.0.0.105')
2553 self.assertRegex(output, 'okey 0.0.0.105')
2554 self.assertNotRegex(output, 'iseq')
2555 self.assertNotRegex(output, 'oseq')
2556 output = check_output('ip -d link show gretun96')
2557 print(output)
2558 self.assertRegex(output, 'gre remote any local any dev dummy98')
2559 self.assertRegex(output, 'ikey 0.0.0.106')
2560 self.assertRegex(output, 'okey 0.0.0.106')
2561 self.assertNotRegex(output, 'iseq')
2562 self.assertNotRegex(output, 'oseq')
2563
2564 touch_network_unit(
2565 '25-gre-tunnel.netdev',
2566 '25-gre-tunnel-local-any.netdev',
2567 '25-gre-tunnel-remote-any.netdev',
2568 '25-gre-tunnel-any-any.netdev')
2569 networkctl_reload()
2570 self.wait_online(
2571 'gretun99:routable',
2572 'gretun98:routable',
2573 'gretun97:routable',
2574 'gretun96:routable',
2575 'dummy98:degraded')
2576
2577 def test_ip6gre_tunnel(self):
2578 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2579 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2580 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2581 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2582 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2583 start_networkd()
2584
2585 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2586
2587 self.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2588
2589 output = check_output('ip -d link show ip6gretun99')
2590 print(output)
2591 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2592 output = check_output('ip -d link show ip6gretun98')
2593 print(output)
2594 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2595 output = check_output('ip -d link show ip6gretun97')
2596 print(output)
2597 self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2598 output = check_output('ip -d link show ip6gretun96')
2599 print(output)
2600 self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
2601
2602 touch_network_unit(
2603 '25-ip6gre-tunnel.netdev',
2604 '25-ip6gre-tunnel-local-any.netdev',
2605 '25-ip6gre-tunnel-remote-any.netdev',
2606 '25-ip6gre-tunnel-any-any.netdev')
2607 networkctl_reload()
2608 self.wait_links(
2609 'dummy98',
2610 'ip6gretun99',
2611 'ip6gretun98',
2612 'ip6gretun97',
2613 'ip6gretun96')
2614
2615 def test_gretap_tunnel(self):
2616 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2617 '25-gretap-tunnel.netdev', '25-tunnel.network',
2618 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2619 start_networkd()
2620 self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2621 self.networkctl_check_unit('gretap99', '25-gretap-tunnel', '25-tunnel')
2622 self.networkctl_check_unit('gretap98', '25-gretap-tunnel-local-any', '25-tunnel-local-any')
2623 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretap')
2624
2625 output = check_output('ip -d link show gretap99')
2626 print(output)
2627 self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2628 self.assertRegex(output, 'ikey 0.0.0.106')
2629 self.assertRegex(output, 'okey 0.0.0.106')
2630 self.assertRegex(output, 'iseq')
2631 self.assertRegex(output, 'oseq')
2632 self.assertIn('nopmtudisc', output)
2633 self.assertIn('ignore-df', output)
2634 output = check_output('ip -d link show gretap98')
2635 print(output)
2636 self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
2637 self.assertRegex(output, 'ikey 0.0.0.107')
2638 self.assertRegex(output, 'okey 0.0.0.107')
2639 self.assertRegex(output, 'iseq')
2640 self.assertRegex(output, 'oseq')
2641
2642 touch_network_unit(
2643 '25-gretap-tunnel.netdev',
2644 '25-gretap-tunnel-local-any.netdev')
2645 networkctl_reload()
2646 self.wait_online(
2647 'gretap99:routable',
2648 'gretap98:routable',
2649 'dummy98:degraded')
2650
2651 def test_ip6gretap_tunnel(self):
2652 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2653 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2654 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2655 start_networkd()
2656 self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2657 self.networkctl_check_unit('ip6gretap99', '25-ip6gretap-tunnel', '25-tunnel')
2658 self.networkctl_check_unit('ip6gretap98', '25-ip6gretap-tunnel-local-any', '25-tunnel-local-any')
2659 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6gretap')
2660
2661 output = check_output('ip -d link show ip6gretap99')
2662 print(output)
2663 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2664 output = check_output('ip -d link show ip6gretap98')
2665 print(output)
2666 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2667
2668 touch_network_unit(
2669 '25-ip6gretap-tunnel.netdev',
2670 '25-ip6gretap-tunnel-local-any.netdev')
2671 networkctl_reload()
2672 self.wait_online(
2673 'ip6gretap99:routable',
2674 'ip6gretap98:routable',
2675 'dummy98:degraded')
2676
2677 def test_vti_tunnel(self):
2678 copy_network_unit('12-dummy.netdev', '25-vti.network',
2679 '25-vti-tunnel.netdev', '25-tunnel.network',
2680 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2681 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2682 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2683 start_networkd()
2684 self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2685 self.networkctl_check_unit('vtitun99', '25-vti-tunnel', '25-tunnel')
2686 self.networkctl_check_unit('vtitun98', '25-vti-tunnel-local-any', '25-tunnel-local-any')
2687 self.networkctl_check_unit('vtitun97', '25-vti-tunnel-remote-any', '25-tunnel-remote-any')
2688 self.networkctl_check_unit('vtitun96', '25-vti-tunnel-any-any', '25-tunnel-any-any')
2689 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti')
2690
2691 output = check_output('ip -d link show vtitun99')
2692 print(output)
2693 self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2694 output = check_output('ip -d link show vtitun98')
2695 print(output)
2696 self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
2697 output = check_output('ip -d link show vtitun97')
2698 print(output)
2699 self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
2700 output = check_output('ip -d link show vtitun96')
2701 print(output)
2702 self.assertRegex(output, 'vti remote any local any dev dummy98')
2703
2704 touch_network_unit(
2705 '25-vti-tunnel.netdev',
2706 '25-vti-tunnel-local-any.netdev',
2707 '25-vti-tunnel-remote-any.netdev',
2708 '25-vti-tunnel-any-any.netdev')
2709 networkctl_reload()
2710 self.wait_online(
2711 'vtitun99:routable',
2712 'vtitun98:routable',
2713 'vtitun97:routable',
2714 'vtitun96:routable',
2715 'dummy98:degraded')
2716
2717 def test_vti6_tunnel(self):
2718 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2719 '25-vti6-tunnel.netdev', '25-tunnel.network',
2720 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2721 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2722 start_networkd()
2723 self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2724 self.networkctl_check_unit('vti6tun99', '25-vti6-tunnel', '25-tunnel')
2725 self.networkctl_check_unit('vti6tun98', '25-vti6-tunnel-local-any', '25-tunnel-local-any')
2726 self.networkctl_check_unit('vti6tun97', '25-vti6-tunnel-remote-any', '25-tunnel-remote-any')
2727 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti6')
2728
2729 output = check_output('ip -d link show vti6tun99')
2730 print(output)
2731 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2732 output = check_output('ip -d link show vti6tun98')
2733 print(output)
2734 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2735 output = check_output('ip -d link show vti6tun97')
2736 print(output)
2737 self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2738
2739 touch_network_unit(
2740 '25-vti6-tunnel.netdev',
2741 '25-vti6-tunnel-local-any.netdev',
2742 '25-vti6-tunnel-remote-any.netdev')
2743 networkctl_reload()
2744 self.wait_online(
2745 'vti6tun99:routable',
2746 'vti6tun98:routable',
2747 'vti6tun97:routable',
2748 'dummy98:degraded')
2749
2750 def _test_ip6tnl_tunnel(self, mode):
2751 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2752 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2753 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2754 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2755 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2756 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2757 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2758
2759 if mode:
2760 for netdev in ['25-ip6tnl-tunnel.netdev',
2761 '25-ip6tnl-tunnel-local-any.netdev',
2762 '25-ip6tnl-tunnel-remote-any.netdev',
2763 '25-ip6tnl-tunnel-local-slaac.netdev',
2764 '25-ip6tnl-tunnel-external.netdev']:
2765 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2766 f.write(f'[Tunnel]\nMode={mode}\n')
2767 else:
2768 mode = 'any' # kernel default
2769
2770 start_networkd()
2771 self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2772 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2773 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2774 self.networkctl_check_unit('ip6tnl99', '25-ip6tnl-tunnel', '25-tunnel')
2775 self.networkctl_check_unit('ip6tnl98', '25-ip6tnl-tunnel-local-any', '25-tunnel-local-any')
2776 self.networkctl_check_unit('ip6tnl97', '25-ip6tnl-tunnel-remote-any', '25-tunnel-remote-any')
2777 self.networkctl_check_unit('ip6tnl-slaac', '25-ip6tnl-tunnel-local-slaac', '25-ip6tnl-tunnel-local-slaac')
2778 self.networkctl_check_unit('ip6tnl-external', '25-ip6tnl-tunnel-external', '26-netdev-link-local-addressing-yes')
2779 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6tnl')
2780 self.networkctl_check_unit('veth99', '25-veth', '25-ip6tnl-slaac')
2781 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
2782
2783 output = check_output('ip -d link show ip6tnl99')
2784 print(output)
2785 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
2786 output = check_output('ip -d link show ip6tnl98')
2787 print(output)
2788 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98', output)
2789 output = check_output('ip -d link show ip6tnl97')
2790 print(output)
2791 self.assertIn(f'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98', output)
2792 output = check_output('ip -d link show ip6tnl-external')
2793 print(output)
2794 self.assertIn('ip6tnl-external@NONE:', output)
2795 self.assertIn('ip6tnl external ', output)
2796 output = check_output('ip -d link show ip6tnl-slaac')
2797 print(output)
2798 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
2799
2800 output = check_output('ip -6 address show veth99')
2801 print(output)
2802 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
2803
2804 output = check_output('ip -4 route show default')
2805 print(output)
2806 self.assertIn('default dev ip6tnl-slaac proto static', output)
2807
2808 touch_network_unit(
2809 '25-ip6tnl-tunnel.netdev',
2810 '25-ip6tnl-tunnel-local-any.netdev',
2811 '25-ip6tnl-tunnel-remote-any.netdev',
2812 '25-ip6tnl-tunnel-local-slaac.netdev',
2813 '25-ip6tnl-tunnel-external.netdev')
2814 networkctl_reload()
2815 self.wait_online(
2816 'ip6tnl99:routable',
2817 'ip6tnl98:routable',
2818 'ip6tnl97:routable',
2819 'ip6tnl-slaac:degraded',
2820 'ip6tnl-external:degraded',
2821 'dummy98:degraded',
2822 'veth99:routable',
2823 'veth-peer:degraded')
2824
2825 def test_ip6tnl_tunnel(self):
2826 first = True
2827 for mode in [None, 'ipip6', 'ip6ip6', 'any']:
2828 if first:
2829 first = False
2830 else:
2831 self.tearDown()
2832
2833 print(f'### test_ip6tnl_tunnel(mode={mode})')
2834 with self.subTest(mode=mode):
2835 self._test_ip6tnl_tunnel(mode)
2836
2837 def _test_sit_tunnel(self, mode):
2838 copy_network_unit('12-dummy.netdev', '25-sit.network',
2839 '25-sit-tunnel.netdev', '25-tunnel.network',
2840 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2841 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2842 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2843
2844 if mode:
2845 for netdev in ['25-sit-tunnel.netdev',
2846 '25-sit-tunnel-local-any.netdev',
2847 '25-sit-tunnel-remote-any.netdev',
2848 '25-sit-tunnel-any-any.netdev']:
2849 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2850 f.write(f'[Tunnel]\nMode={mode}\n')
2851 else:
2852 mode = 'ip6ip' # kernel default
2853
2854 start_networkd()
2855 self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2856 self.networkctl_check_unit('sittun99', '25-sit-tunnel', '25-tunnel')
2857 self.networkctl_check_unit('sittun98', '25-sit-tunnel-local-any', '25-tunnel-local-any')
2858 self.networkctl_check_unit('sittun97', '25-sit-tunnel-remote-any', '25-tunnel-remote-any')
2859 self.networkctl_check_unit('sittun96', '25-sit-tunnel-any-any', '25-tunnel-any-any')
2860 self.networkctl_check_unit('dummy98', '12-dummy', '25-sit')
2861
2862 output = check_output('ip -d link show sittun99')
2863 print(output)
2864 self.assertIn(f'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98', output)
2865 output = check_output('ip -d link show sittun98')
2866 print(output)
2867 self.assertIn(f'sit {mode} remote 10.65.223.239 local any dev dummy98', output)
2868 output = check_output('ip -d link show sittun97')
2869 print(output)
2870 self.assertIn(f'sit {mode} remote any local 10.65.223.238 dev dummy98', output)
2871 output = check_output('ip -d link show sittun96')
2872 print(output)
2873 self.assertIn(f'sit {mode} remote any local any dev dummy98', output)
2874
2875 touch_network_unit(
2876 '25-sit-tunnel.netdev',
2877 '25-sit-tunnel-local-any.netdev',
2878 '25-sit-tunnel-remote-any.netdev',
2879 '25-sit-tunnel-any-any.netdev')
2880 networkctl_reload()
2881 self.wait_online(
2882 'sittun99:routable',
2883 'sittun98:routable',
2884 'sittun97:routable',
2885 'sittun96:routable',
2886 'dummy98:degraded')
2887
2888 def test_sit_tunnel(self):
2889 first = True
2890 for mode in [None, 'ipip', 'ip6ip', 'any']:
2891 if first:
2892 first = False
2893 else:
2894 self.tearDown()
2895
2896 print(f'### test_sit_tunnel(mode={mode})')
2897 with self.subTest(mode=mode):
2898 self._test_sit_tunnel(mode)
2899
2900 def test_isatap_tunnel(self):
2901 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2902 '25-isatap-tunnel.netdev', '25-tunnel.network')
2903 start_networkd()
2904 self.wait_online('isataptun99:routable', 'dummy98:degraded')
2905 self.networkctl_check_unit('isataptun99', '25-isatap-tunnel', '25-tunnel')
2906 self.networkctl_check_unit('dummy98', '12-dummy', '25-isatap')
2907
2908 output = check_output('ip -d link show isataptun99')
2909 print(output)
2910 self.assertRegex(output, "isatap ")
2911
2912 touch_network_unit('25-isatap-tunnel.netdev')
2913 networkctl_reload()
2914 self.wait_online('isataptun99:routable', 'dummy98:degraded')
2915
2916 def test_6rd_tunnel(self):
2917 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2918 '25-6rd-tunnel.netdev', '25-tunnel.network')
2919 start_networkd()
2920 self.wait_online('sittun99:routable', 'dummy98:degraded')
2921 self.networkctl_check_unit('sittun99', '25-6rd-tunnel', '25-tunnel')
2922 self.networkctl_check_unit('dummy98', '12-dummy', '25-6rd')
2923
2924 output = check_output('ip -d link show sittun99')
2925 print(output)
2926 self.assertRegex(output, '6rd-prefix 2602::/24')
2927
2928 touch_network_unit('25-6rd-tunnel.netdev')
2929 networkctl_reload()
2930 self.wait_online('sittun99:routable', 'dummy98:degraded')
2931
2932 @expectedFailureIfERSPANv0IsNotSupported()
2933 def test_erspan_tunnel_v0(self):
2934 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2935 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2936 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2937 start_networkd()
2938 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2939 self.networkctl_check_unit('erspan99', '25-erspan0-tunnel', '25-tunnel')
2940 self.networkctl_check_unit('erspan98', '25-erspan0-tunnel-local-any', '25-tunnel-local-any')
2941 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2942
2943 output = check_output('ip -d link show erspan99')
2944 print(output)
2945 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2946 self.assertIn('erspan_ver 0', output)
2947 self.assertNotIn('erspan_index 123', output)
2948 self.assertNotIn('erspan_dir ingress', output)
2949 self.assertNotIn('erspan_hwid 1f', output)
2950 self.assertIn('ikey 0.0.0.101', output)
2951 self.assertIn('iseq', output)
2952 self.assertIn('nopmtudisc', output)
2953 self.assertIn('ignore-df', output)
2954 output = check_output('ip -d link show erspan98')
2955 print(output)
2956 self.assertIn('erspan remote 172.16.1.100 local any', output)
2957 self.assertIn('erspan_ver 0', output)
2958 self.assertNotIn('erspan_index 124', output)
2959 self.assertNotIn('erspan_dir egress', output)
2960 self.assertNotIn('erspan_hwid 2f', output)
2961 self.assertIn('ikey 0.0.0.102', output)
2962 self.assertIn('iseq', output)
2963
2964 touch_network_unit(
2965 '25-erspan0-tunnel.netdev',
2966 '25-erspan0-tunnel-local-any.netdev')
2967 networkctl_reload()
2968 self.wait_online(
2969 'erspan99:routable',
2970 'erspan98:routable',
2971 'dummy98:degraded')
2972
2973 def test_erspan_tunnel_v1(self):
2974 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2975 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2976 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2977 start_networkd()
2978 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2979 self.networkctl_check_unit('erspan99', '25-erspan1-tunnel', '25-tunnel')
2980 self.networkctl_check_unit('erspan98', '25-erspan1-tunnel-local-any', '25-tunnel-local-any')
2981 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2982
2983 output = check_output('ip -d link show erspan99')
2984 print(output)
2985 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2986 self.assertIn('erspan_ver 1', output)
2987 self.assertIn('erspan_index 123', output)
2988 self.assertNotIn('erspan_dir ingress', output)
2989 self.assertNotIn('erspan_hwid 1f', output)
2990 self.assertIn('ikey 0.0.0.101', output)
2991 self.assertIn('okey 0.0.0.101', output)
2992 self.assertIn('iseq', output)
2993 self.assertIn('oseq', output)
2994 output = check_output('ip -d link show erspan98')
2995 print(output)
2996 self.assertIn('erspan remote 172.16.1.100 local any', output)
2997 self.assertIn('erspan_ver 1', output)
2998 self.assertIn('erspan_index 124', output)
2999 self.assertNotIn('erspan_dir egress', output)
3000 self.assertNotIn('erspan_hwid 2f', output)
3001 self.assertIn('ikey 0.0.0.102', output)
3002 self.assertIn('okey 0.0.0.102', output)
3003 self.assertIn('iseq', output)
3004 self.assertIn('oseq', output)
3005
3006 touch_network_unit(
3007 '25-erspan1-tunnel.netdev',
3008 '25-erspan1-tunnel-local-any.netdev')
3009 networkctl_reload()
3010 self.wait_online(
3011 'erspan99:routable',
3012 'erspan98:routable',
3013 'dummy98:degraded')
3014
3015 @expectedFailureIfERSPANv2IsNotSupported()
3016 def test_erspan_tunnel_v2(self):
3017 copy_network_unit('12-dummy.netdev', '25-erspan.network',
3018 '25-erspan2-tunnel.netdev', '25-tunnel.network',
3019 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
3020 start_networkd()
3021 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
3022 self.networkctl_check_unit('erspan99', '25-erspan2-tunnel', '25-tunnel')
3023 self.networkctl_check_unit('erspan98', '25-erspan2-tunnel-local-any', '25-tunnel-local-any')
3024 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
3025
3026 output = check_output('ip -d link show erspan99')
3027 print(output)
3028 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
3029 self.assertIn('erspan_ver 2', output)
3030 self.assertNotIn('erspan_index 123', output)
3031 self.assertIn('erspan_dir ingress', output)
3032 self.assertIn('erspan_hwid 0x1f', output)
3033 self.assertIn('ikey 0.0.0.101', output)
3034 self.assertIn('okey 0.0.0.101', output)
3035 self.assertIn('iseq', output)
3036 self.assertIn('oseq', output)
3037 output = check_output('ip -d link show erspan98')
3038 print(output)
3039 self.assertIn('erspan remote 172.16.1.100 local any', output)
3040 self.assertIn('erspan_ver 2', output)
3041 self.assertNotIn('erspan_index 124', output)
3042 self.assertIn('erspan_dir egress', output)
3043 self.assertIn('erspan_hwid 0x2f', output)
3044 self.assertIn('ikey 0.0.0.102', output)
3045 self.assertIn('okey 0.0.0.102', output)
3046 self.assertIn('iseq', output)
3047 self.assertIn('oseq', output)
3048
3049 touch_network_unit(
3050 '25-erspan2-tunnel.netdev',
3051 '25-erspan2-tunnel-local-any.netdev')
3052 networkctl_reload()
3053 self.wait_online(
3054 'erspan99:routable',
3055 'erspan98:routable',
3056 'dummy98:degraded')
3057
3058 def test_tunnel_independent(self):
3059 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
3060 start_networkd()
3061
3062 self.wait_online('ipiptun99:carrier')
3063 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent', '26-netdev-link-local-addressing-yes')
3064
3065 def test_tunnel_independent_loopback(self):
3066 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
3067 start_networkd()
3068
3069 self.wait_online('ipiptun99:carrier')
3070 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent-loopback', '26-netdev-link-local-addressing-yes')
3071
3072 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
3073 def test_xfrm(self):
3074 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
3075 '25-xfrm.netdev', '25-xfrm-independent.netdev',
3076 '26-netdev-link-local-addressing-yes.network')
3077 start_networkd()
3078
3079 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
3080 self.networkctl_check_unit('dummy98', '12-dummy', '25-xfrm')
3081 self.networkctl_check_unit('xfrm98', '25-xfrm', '26-netdev-link-local-addressing-yes')
3082 self.networkctl_check_unit('xfrm99', '25-xfrm-independent', '26-netdev-link-local-addressing-yes')
3083
3084 output = check_output('ip -d link show dev xfrm98')
3085 print(output)
3086 self.assertIn('xfrm98@dummy98:', output)
3087 self.assertIn('xfrm if_id 0x98 ', output)
3088
3089 output = check_output('ip -d link show dev xfrm99')
3090 print(output)
3091 self.assertIn('xfrm99@lo:', output)
3092 self.assertIn('xfrm if_id 0x99 ', output)
3093
3094 touch_network_unit('25-xfrm.netdev')
3095 networkctl_reload()
3096 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
3097
3098 @expectedFailureIfModuleIsNotAvailable('fou')
3099 def test_fou(self):
3100 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3101 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3102 '25-fou-gre.netdev', '25-fou-gretap.netdev')
3103 start_networkd()
3104
3105 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
3106 self.networkctl_check_unit('ipiptun96', '25-fou-ipip')
3107 self.networkctl_check_unit('sittun96', '25-fou-sit')
3108 self.networkctl_check_unit('gretun96', '25-fou-gre')
3109 self.networkctl_check_unit('gretap96', '25-fou-gretap')
3110
3111 output = check_output('ip fou show')
3112 print(output)
3113 self.assertRegex(output, 'port 55555 ipproto 4')
3114 self.assertRegex(output, 'port 55556 ipproto 47')
3115
3116 output = check_output('ip -d link show ipiptun96')
3117 print(output)
3118 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
3119 output = check_output('ip -d link show sittun96')
3120 print(output)
3121 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
3122 output = check_output('ip -d link show gretun96')
3123 print(output)
3124 self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
3125 output = check_output('ip -d link show gretap96')
3126 print(output)
3127 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
3128
3129 touch_network_unit(
3130 '25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3131 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3132 '25-fou-gre.netdev', '25-fou-gretap.netdev')
3133 networkctl_reload()
3134 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
3135
3136 def test_vxlan(self):
3137 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
3138 '25-vxlan.netdev', '25-vxlan.network',
3139 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
3140 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
3141 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
3142 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network',
3143 '25-vxlan-external.netdev', '25-vxlan-external.network')
3144 start_networkd()
3145
3146 self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
3147 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded',
3148 'vxlan-external:degraded')
3149 self.networkctl_check_unit('test1', '11-dummy', '25-vxlan-test1')
3150 self.networkctl_check_unit('veth99', '25-veth', '25-vxlan-veth99')
3151 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
3152 self.networkctl_check_unit('vxlan99', '25-vxlan', '25-vxlan')
3153 self.networkctl_check_unit('vxlan98', '25-vxlan-independent', '26-netdev-link-local-addressing-yes')
3154 self.networkctl_check_unit('vxlan97', '25-vxlan-ipv6', '25-vxlan-ipv6')
3155 self.networkctl_check_unit('vxlan-slaac', '25-vxlan-local-slaac', '25-vxlan-local-slaac')
3156 self.networkctl_check_unit('vxlan-external', '25-vxlan-external', '25-vxlan-external')
3157
3158 output = check_output('ip -d -d link show vxlan99')
3159 print(output)
3160 self.assertIn('999', output)
3161 self.assertIn('5555', output)
3162 self.assertIn('l2miss', output)
3163 self.assertIn('l3miss', output)
3164 self.assertIn('gbp', output)
3165 # Since [0] some of the options use slightly different names and some
3166 # options with default values are shown only if the -d(etails) setting
3167 # is repeated
3168 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
3169 self.assertRegex(output, '(udpcsum|udp_csum)')
3170 self.assertRegex(output, '(udp6zerocsumtx|udp_zero_csum6_tx)')
3171 self.assertRegex(output, '(udp6zerocsumrx|udp_zero_csum6_rx)')
3172 self.assertRegex(output, '(remcsumtx|remcsum_tx)')
3173 self.assertRegex(output, '(remcsumrx|remcsum_rx)')
3174
3175 output = check_output('bridge fdb show dev vxlan99')
3176 print(output)
3177 self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
3178 self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
3179 self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
3180
3181 output = networkctl_status('vxlan99')
3182 print(output)
3183 self.assertIn('VNI: 999', output)
3184 self.assertIn('Destination Port: 5555', output)
3185 self.assertIn('Underlying Device: test1', output)
3186
3187 output = check_output('bridge fdb show dev vxlan97')
3188 print(output)
3189 self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
3190 self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
3191 self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
3192
3193 output = check_output('ip -d link show vxlan-slaac')
3194 print(output)
3195 self.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
3196
3197 output = check_output('ip -6 address show veth99')
3198 print(output)
3199 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
3200
3201 output = check_output('ip -d link show vxlan-external')
3202 print(output)
3203 self.assertIn('id 0 ', output)
3204 self.assertIn('external', output)
3205 self.assertIn('vnifilter', output)
3206
3207 @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
3208 def test_macsec(self):
3209 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
3210 '26-macsec.network', '12-dummy.netdev')
3211 start_networkd()
3212
3213 self.wait_online('dummy98:degraded', 'macsec99:routable')
3214 self.networkctl_check_unit('dummy98', '12-dummy', '26-macsec')
3215 self.networkctl_check_unit('macsec99', '25-macsec', '25-macsec')
3216
3217 output = check_output('ip -d link show macsec99')
3218 print(output)
3219 self.assertRegex(output, 'macsec99@dummy98')
3220 self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
3221 self.assertRegex(output, 'encrypt on')
3222
3223 output = check_output('ip macsec show macsec99')
3224 print(output)
3225 self.assertRegex(output, 'encrypt on')
3226 self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
3227 self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
3228 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
3229 self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
3230 self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
3231 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
3232 self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
3233 self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
3234 self.assertNotRegex(output, 'key 02030405067080900000000000000000')
3235 self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
3236 self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
3237
3238 touch_network_unit('25-macsec.netdev')
3239 networkctl_reload()
3240 self.wait_online('dummy98:degraded', 'macsec99:routable')
3241
3242 def test_nlmon(self):
3243 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
3244 start_networkd()
3245
3246 self.wait_online('nlmon99:carrier')
3247 self.networkctl_check_unit('nlmon99', '25-nlmon', '26-netdev-link-local-addressing-yes')
3248
3249 touch_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
3250 networkctl_reload()
3251 self.wait_online('nlmon99:carrier')
3252
3253 @expectedFailureIfModuleIsNotAvailable('ifb')
3254 def test_ifb(self):
3255 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
3256 start_networkd()
3257
3258 self.wait_online('ifb99:degraded')
3259 self.networkctl_check_unit('ifb99', '25-ifb', '26-netdev-link-local-addressing-yes')
3260
3261 touch_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
3262 networkctl_reload()
3263 self.wait_online('ifb99:degraded')
3264
3265 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3266 def test_rps_cpu_1(self):
3267 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
3268 start_networkd()
3269
3270 self.wait_online('dummy98:carrier')
3271 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-1')
3272
3273 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3274 print(output)
3275 self.assertEqual(int(output.replace(',', ''), base=16), 2)
3276
3277 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3278 def test_rps_cpu_0_1(self):
3279 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
3280 start_networkd()
3281
3282 self.wait_online('dummy98:carrier')
3283 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-1')
3284
3285 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3286 print(output)
3287 self.assertEqual(int(output.replace(',', ''), base=16), 3)
3288
3289 @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test")
3290 def test_rps_cpu_multi(self):
3291 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
3292 start_networkd()
3293
3294 self.wait_online('dummy98:carrier')
3295 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-multi')
3296
3297 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3298 print(output)
3299 self.assertEqual(int(output.replace(',', ''), base=16), 15)
3300
3301 def test_rps_cpu(self):
3302 cpu_count = os.cpu_count()
3303
3304 copy_network_unit('12-dummy.netdev', '12-dummy.network')
3305 start_networkd()
3306
3307 self.wait_online('dummy98:carrier')
3308 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy')
3309
3310 # 0
3311 copy_network_unit('25-rps-cpu-0.link')
3312 udevadm_trigger('/sys/class/net/dummy98')
3313 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0')
3314 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3315 print(output)
3316 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3317 remove_network_unit('25-rps-cpu-0.link')
3318
3319 # all
3320 copy_network_unit('25-rps-cpu-all.link')
3321 udevadm_trigger('/sys/class/net/dummy98')
3322 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
3323 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3324 print(output)
3325 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3326 remove_network_unit('25-rps-cpu-all.link')
3327
3328 # disable
3329 copy_network_unit('24-rps-cpu-disable.link')
3330 udevadm_trigger('/sys/class/net/dummy98')
3331 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-disable')
3332 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3333 print(output)
3334 self.assertEqual(int(output.replace(',', ''), base=16), 0)
3335 remove_network_unit('24-rps-cpu-disable.link')
3336
3337 # set all again
3338 copy_network_unit('25-rps-cpu-all.link')
3339 udevadm_trigger('/sys/class/net/dummy98')
3340 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
3341 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3342 print(output)
3343 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3344 remove_network_unit('25-rps-cpu-all.link')
3345
3346 # empty -> unchanged
3347 copy_network_unit('24-rps-cpu-empty.link')
3348 udevadm_trigger('/sys/class/net/dummy98')
3349 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-empty')
3350 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3351 print(output)
3352 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3353 remove_network_unit('24-rps-cpu-empty.link')
3354
3355 # 0, then empty -> unchanged
3356 copy_network_unit('25-rps-cpu-0-empty.link')
3357 udevadm_trigger('/sys/class/net/dummy98')
3358 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-empty')
3359 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3360 print(output)
3361 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3362 remove_network_unit('25-rps-cpu-0-empty.link')
3363
3364 # 0, then invalid -> 0
3365 copy_network_unit('25-rps-cpu-0-invalid.link')
3366 udevadm_trigger('/sys/class/net/dummy98')
3367 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-invalid')
3368 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3369 print(output)
3370 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3371 remove_network_unit('25-rps-cpu-0-invalid.link')
3372
3373 # invalid -> unchanged
3374 copy_network_unit('24-rps-cpu-invalid.link')
3375 udevadm_trigger('/sys/class/net/dummy98')
3376 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-invalid')
3377 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3378 print(output)
3379 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3380 remove_network_unit('24-rps-cpu-invalid.link')
3381
3382 class NetworkdL2TPTests(unittest.TestCase, Utilities):
3383
3384 def setUp(self):
3385 setup_common()
3386
3387 def tearDown(self):
3388 tear_down_common()
3389
3390 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
3391 def test_l2tp_udp(self):
3392 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3393 '25-l2tp-udp.netdev', '25-l2tp.network')
3394 start_networkd()
3395
3396 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
3397 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3398 self.networkctl_check_unit('l2tp-ses1', '25-l2tp-udp', '25-l2tp')
3399 self.networkctl_check_unit('l2tp-ses2', '25-l2tp-udp', '25-l2tp')
3400
3401 output = check_output('ip l2tp show tunnel tunnel_id 10')
3402 print(output)
3403 self.assertRegex(output, "Tunnel 10, encap UDP")
3404 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3405 self.assertRegex(output, "Peer tunnel 11")
3406 self.assertRegex(output, "UDP source / dest ports: 3000/4000")
3407 self.assertRegex(output, "UDP checksum: enabled")
3408
3409 output = check_output('ip l2tp show session tid 10 session_id 15')
3410 print(output)
3411 self.assertRegex(output, "Session 15 in tunnel 10")
3412 self.assertRegex(output, "Peer session 16, tunnel 11")
3413 self.assertRegex(output, "interface name: l2tp-ses1")
3414
3415 output = check_output('ip l2tp show session tid 10 session_id 17')
3416 print(output)
3417 self.assertRegex(output, "Session 17 in tunnel 10")
3418 self.assertRegex(output, "Peer session 18, tunnel 11")
3419 self.assertRegex(output, "interface name: l2tp-ses2")
3420
3421 touch_network_unit(
3422 '11-dummy.netdev', '25-l2tp-dummy.network',
3423 '25-l2tp-udp.netdev', '25-l2tp.network')
3424 networkctl_reload()
3425 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
3426
3427 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
3428 def test_l2tp_ip(self):
3429 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3430 '25-l2tp-ip.netdev', '25-l2tp.network')
3431 start_networkd()
3432
3433 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
3434 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3435 self.networkctl_check_unit('l2tp-ses3', '25-l2tp-ip', '25-l2tp')
3436 self.networkctl_check_unit('l2tp-ses4', '25-l2tp-ip', '25-l2tp')
3437
3438 output = check_output('ip l2tp show tunnel tunnel_id 10')
3439 print(output)
3440 self.assertRegex(output, "Tunnel 10, encap IP")
3441 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3442 self.assertRegex(output, "Peer tunnel 12")
3443
3444 output = check_output('ip l2tp show session tid 10 session_id 25')
3445 print(output)
3446 self.assertRegex(output, "Session 25 in tunnel 10")
3447 self.assertRegex(output, "Peer session 26, tunnel 12")
3448 self.assertRegex(output, "interface name: l2tp-ses3")
3449
3450 output = check_output('ip l2tp show session tid 10 session_id 27')
3451 print(output)
3452 self.assertRegex(output, "Session 27 in tunnel 10")
3453 self.assertRegex(output, "Peer session 28, tunnel 12")
3454 self.assertRegex(output, "interface name: l2tp-ses4")
3455
3456 touch_network_unit(
3457 '11-dummy.netdev', '25-l2tp-dummy.network',
3458 '25-l2tp-ip.netdev', '25-l2tp.network')
3459 networkctl_reload()
3460 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
3461
3462 class NetworkdNetworkTests(unittest.TestCase, Utilities):
3463
3464 def setUp(self):
3465 setup_common()
3466
3467 def tearDown(self):
3468 tear_down_common()
3469
3470 def test_ID_NET_MANAGED_BY(self):
3471 copy_network_unit('11-dummy.netdev', '11-dummy-unmanaged.link', '11-dummy.network')
3472 start_networkd()
3473 self.wait_online('test1:off', setup_state='unmanaged')
3474
3475 check_output('ip link set dev test1 up')
3476 self.wait_online('test1:degraded', setup_state='unmanaged')
3477
3478 check_output('ip link set dev test1 down')
3479 self.wait_online('test1:off', setup_state='unmanaged')
3480
3481 def verify_address_static(
3482 self,
3483 label1: str,
3484 label2: str,
3485 label3: str,
3486 broadcast1: str,
3487 broadcast2: str,
3488 broadcast3: str,
3489 peer1: str,
3490 peer2: str,
3491 peer3: str,
3492 peer4: str,
3493 peer5: str,
3494 peer6: str,
3495 scope1: str,
3496 scope2: str,
3497 deprecated1: str,
3498 deprecated2: str,
3499 deprecated3: str,
3500 deprecated4: str,
3501 route_metric: int,
3502 flag1: str,
3503 flag2: str,
3504 flag3: str,
3505 flag4: str,
3506 ip4_null_16: str,
3507 ip4_null_24: str,
3508 ip6_null_73: str,
3509 ip6_null_74: str,
3510 ):
3511 output = check_output('ip address show dev dummy98')
3512 print(output)
3513
3514 # simple settings
3515 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
3516 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
3517 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
3518 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
3519 self.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output)
3520 self.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output)
3521
3522 # label
3523 self.assertIn(f'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output)
3524 self.assertIn(f'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output)
3525 self.assertIn(f'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output)
3526
3527 # broadcast
3528 self.assertIn(f'inet 10.4.1.1/24{broadcast1} scope global dummy98', output)
3529 self.assertIn(f'inet 10.4.2.1/24{broadcast2} scope global dummy98', output)
3530 self.assertIn(f'inet 10.4.3.1/24{broadcast3} scope global dummy98', output)
3531
3532 # peer
3533 self.assertIn(f'inet 10.5.1.1{peer1} scope global dummy98', output)
3534 self.assertIn(f'inet 10.5.2.1{peer2} scope global dummy98', output)
3535 self.assertIn(f'inet 10.5.3.1{peer3} scope global dummy98', output)
3536 self.assertIn(f'inet6 2001:db8:0:f103::1{peer4} scope global', output)
3537 self.assertIn(f'inet6 2001:db8:0:f103::2{peer5} scope global', output)
3538 self.assertIn(f'inet6 2001:db8:0:f103::3{peer6} scope global', output)
3539
3540 # scope
3541 self.assertIn(f'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output)
3542 self.assertIn(f'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output)
3543
3544 # lifetime
3545 self.assertIn(f'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output)
3546 self.assertIn(f'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output)
3547 self.assertIn(f'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output)
3548 self.assertIn(f'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output)
3549
3550 # route metric
3551 self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
3552 self.assertRegex(output, rf'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
3553
3554 output_route = check_output('ip -4 route show dev dummy98 10.8.1.0/24')
3555 print(output_route)
3556 self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route)
3557
3558 output_route = check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
3559 print(output_route)
3560 self.assertIn(f'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route)
3561
3562 # flags
3563 self.assertIn(f'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output)
3564 self.assertIn(f'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output)
3565 self.assertIn(f'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output)
3566 self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
3567
3568 # null address
3569 self.assertTrue(ip4_null_16.endswith('.0.1'))
3570 prefix16 = ip4_null_16[:-len('.0.1')]
3571 self.assertTrue(ip4_null_24.endswith('.1'))
3572 prefix24 = ip4_null_24[:-len('.1')]
3573 self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
3574 self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
3575 self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
3576 self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
3577
3578 # invalid sections
3579 self.assertNotIn('10.4.4.1', output)
3580 self.assertNotIn('10.5.4.1', output)
3581 self.assertNotIn('10.5.5.1', output)
3582 self.assertNotIn('10.8.2.1', output)
3583 self.assertNotIn('10.9.3.1', output)
3584 self.assertNotIn('2001:db8:0:f101::2', output)
3585 self.assertNotIn('2001:db8:0:f103::4', output)
3586
3587 # netlabel
3588 self.check_netlabel('dummy98', r'10\.10\.1\.0/24')
3589
3590 check_json(networkctl_json())
3591
3592 @expectedFailureIfKernelReturnsInvalidFlags()
3593 def test_address_static(self):
3594 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
3595 self.setup_nftset('addr4', 'ipv4_addr')
3596 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
3597 self.setup_nftset('ifindex', 'iface_index')
3598 start_networkd()
3599
3600 self.wait_online('dummy98:routable')
3601
3602 ip4_null_16 = None
3603 ip4_null_24 = None
3604 output = check_output('ip -4 --json address show dev dummy98')
3605 for i in json.loads(output)[0]['addr_info']:
3606 if i['label'] == 'subnet16':
3607 ip4_null_16 = i['local']
3608 elif i['label'] == 'subnet24':
3609 ip4_null_24 = i['local']
3610 self.assertTrue(ip4_null_16.endswith('.0.1'))
3611 self.assertTrue(ip4_null_24.endswith('.1'))
3612
3613 ip6_null_73 = None
3614 ip6_null_74 = None
3615 output = check_output('ip -6 --json address show dev dummy98')
3616 for i in json.loads(output)[0]['addr_info']:
3617 if i['prefixlen'] == 73:
3618 ip6_null_73 = i['local']
3619 elif i['prefixlen'] == 74:
3620 ip6_null_74 = i['local']
3621 self.assertTrue(ip6_null_73.endswith(':1'))
3622 self.assertTrue(ip6_null_74.endswith(':1'))
3623
3624 self.verify_address_static(
3625 label1='label1',
3626 label2='label2',
3627 label3='dummy98',
3628 broadcast1='',
3629 broadcast2=' brd 10.4.2.255',
3630 broadcast3=' brd 10.4.3.63',
3631 peer1=' peer 10.5.1.101/24',
3632 peer2=' peer 10.5.2.101/24',
3633 peer3='/24 brd 10.5.3.255',
3634 peer4=' peer 2001:db8:0:f103::101/128',
3635 peer5=' peer 2001:db8:0:f103::102/128',
3636 peer6='/128',
3637 scope1='global',
3638 scope2='link',
3639 deprecated1='',
3640 deprecated2=' deprecated',
3641 deprecated3='',
3642 deprecated4=' deprecated',
3643 route_metric=128,
3644 flag1=' noprefixroute',
3645 flag2='',
3646 flag3=' noprefixroute',
3647 flag4=' home mngtmpaddr',
3648 ip4_null_16=ip4_null_16,
3649 ip4_null_24=ip4_null_24,
3650 ip6_null_73=ip6_null_73,
3651 ip6_null_74=ip6_null_74,
3652 )
3653 # nft set
3654 self.check_nftset('addr4', r'10\.10\.1\.1')
3655 self.check_nftset('network4', r'10\.10\.1\.0/24')
3656 self.check_nftset('ifindex', 'dummy98')
3657
3658 self.teardown_nftset('addr4', 'network4', 'ifindex')
3659
3660 copy_network_unit('25-address-static.network.d/10-override.conf')
3661 networkctl_reload()
3662 self.wait_online('dummy98:routable')
3663 self.verify_address_static(
3664 label1='new-label1',
3665 label2='dummy98',
3666 label3='new-label3',
3667 broadcast1=' brd 10.4.1.255',
3668 broadcast2='',
3669 broadcast3=' brd 10.4.3.31',
3670 peer1=' peer 10.5.1.102/24',
3671 peer2='/24 brd 10.5.2.255',
3672 peer3=' peer 10.5.3.102/24',
3673 peer4=' peer 2001:db8:0:f103::201/128',
3674 peer5='/128',
3675 peer6=' peer 2001:db8:0:f103::203/128',
3676 scope1='link',
3677 scope2='global',
3678 deprecated1=' deprecated',
3679 deprecated2='',
3680 deprecated3=' deprecated',
3681 deprecated4='',
3682 route_metric=256,
3683 flag1='',
3684 flag2=' noprefixroute',
3685 flag3=' home mngtmpaddr',
3686 flag4=' noprefixroute',
3687 ip4_null_16=ip4_null_16,
3688 ip4_null_24=ip4_null_24,
3689 ip6_null_73=ip6_null_73,
3690 ip6_null_74=ip6_null_74,
3691 )
3692
3693 networkctl_reconfigure('dummy98')
3694 self.wait_online('dummy98:routable')
3695 self.verify_address_static(
3696 label1='new-label1',
3697 label2='dummy98',
3698 label3='new-label3',
3699 broadcast1=' brd 10.4.1.255',
3700 broadcast2='',
3701 broadcast3=' brd 10.4.3.31',
3702 peer1=' peer 10.5.1.102/24',
3703 peer2='/24 brd 10.5.2.255',
3704 peer3=' peer 10.5.3.102/24',
3705 peer4=' peer 2001:db8:0:f103::201/128',
3706 peer5='/128',
3707 peer6=' peer 2001:db8:0:f103::203/128',
3708 scope1='link',
3709 scope2='global',
3710 deprecated1=' deprecated',
3711 deprecated2='',
3712 deprecated3=' deprecated',
3713 deprecated4='',
3714 route_metric=256,
3715 flag1='',
3716 flag2=' noprefixroute',
3717 flag3=' home mngtmpaddr',
3718 flag4=' noprefixroute',
3719 ip4_null_16=ip4_null_16,
3720 ip4_null_24=ip4_null_24,
3721 ip6_null_73=ip6_null_73,
3722 ip6_null_74=ip6_null_74,
3723 )
3724
3725 # Tests for #20891.
3726 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
3727 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
3728 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
3729 output = check_output('ip address show dev dummy98')
3730 print(output)
3731 self.assertNotRegex(output, '10.7.1.1/24 .* deprecated')
3732 self.assertNotRegex(output, '2001:db8:0:f104::1/64 .* deprecated')
3733
3734 # 2. reconfigure the interface, and check the deprecated flag is set again
3735 networkctl_reconfigure('dummy98')
3736 self.wait_online('dummy98:routable')
3737 self.verify_address_static(
3738 label1='new-label1',
3739 label2='dummy98',
3740 label3='new-label3',
3741 broadcast1=' brd 10.4.1.255',
3742 broadcast2='',
3743 broadcast3=' brd 10.4.3.31',
3744 peer1=' peer 10.5.1.102/24',
3745 peer2='/24 brd 10.5.2.255',
3746 peer3=' peer 10.5.3.102/24',
3747 peer4=' peer 2001:db8:0:f103::201/128',
3748 peer5='/128',
3749 peer6=' peer 2001:db8:0:f103::203/128',
3750 scope1='link',
3751 scope2='global',
3752 deprecated1=' deprecated',
3753 deprecated2='',
3754 deprecated3=' deprecated',
3755 deprecated4='',
3756 route_metric=256,
3757 flag1='',
3758 flag2=' noprefixroute',
3759 flag3=' home mngtmpaddr',
3760 flag4=' noprefixroute',
3761 ip4_null_16=ip4_null_16,
3762 ip4_null_24=ip4_null_24,
3763 ip6_null_73=ip6_null_73,
3764 ip6_null_74=ip6_null_74,
3765 )
3766
3767 # test for ENOBUFS issue #17012 (with reload)
3768 copy_network_unit('25-address-static.network.d/10-many-address.conf')
3769 networkctl_reload()
3770 self.wait_online('dummy98:routable')
3771 output = check_output('ip -4 address show dev dummy98')
3772 for i in range(1, 254):
3773 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3774
3775 # (with reconfigure)
3776 networkctl_reconfigure('dummy98')
3777 self.wait_online('dummy98:routable')
3778 output = check_output('ip -4 address show dev dummy98')
3779 for i in range(1, 254):
3780 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3781
3782 # test for an empty string assignment for Address= in [Network]
3783 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
3784 networkctl_reload()
3785 self.wait_online('dummy98:routable')
3786 output = check_output('ip -4 address show dev dummy98')
3787 for i in range(1, 254):
3788 self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3789 self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
3790
3791 def test_address_ipv4acd(self):
3792 check_output('ip netns add ns99')
3793 check_output('ip link add veth99 type veth peer veth-peer')
3794 check_output('ip link set veth-peer netns ns99')
3795 check_output('ip link set veth99 up')
3796 check_output('ip netns exec ns99 ip link set veth-peer up')
3797 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
3798
3799 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False)
3800 start_networkd()
3801 self.wait_online('veth99:routable')
3802
3803 output = check_output('ip -4 address show dev veth99')
3804 print(output)
3805 self.assertNotIn('192.168.100.10/24', output)
3806 self.assertIn('192.168.100.11/24', output)
3807
3808 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
3809 networkctl_reload()
3810 self.wait_operstate('veth99', operstate='routable', setup_state='configuring', setup_timeout=10)
3811
3812 output = check_output('ip -4 address show dev veth99')
3813 print(output)
3814 self.assertNotIn('192.168.100.10/24', output)
3815 self.assertIn('192.168.100.11/24', output)
3816
3817 def test_address_peer_ipv4(self):
3818 # test for issue #17304
3819 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
3820
3821 for trial in range(2):
3822 if trial == 0:
3823 start_networkd()
3824 else:
3825 restart_networkd()
3826
3827 self.wait_online('dummy98:routable')
3828
3829 output = check_output('ip -4 address show dev dummy98')
3830 self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
3831
3832 @expectedFailureIfModuleIsNotAvailable('vrf')
3833 def test_prefix_route(self):
3834 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3835 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3836 '25-vrf.netdev', '25-vrf.network')
3837 for trial in range(2):
3838 if trial == 0:
3839 start_networkd()
3840 else:
3841 restart_networkd()
3842
3843 self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
3844
3845 output = check_output('ip route show table 42 dev dummy98')
3846 print('### ip route show table 42 dev dummy98')
3847 print(output)
3848 self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
3849 self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3850 self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3851 self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3852 self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
3853 self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3854 self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3855 output = check_output('ip -6 route show table 42 dev dummy98')
3856 print('### ip -6 route show table 42 dev dummy98')
3857 print(output)
3858 if trial == 0:
3859 # Kernel's bug?
3860 self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3861 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3862 self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3863 self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3864 self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3865 self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3866 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3867 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3868
3869 print()
3870
3871 output = check_output('ip route show dev test1')
3872 print('### ip route show dev test1')
3873 print(output)
3874 self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3875 output = check_output('ip route show table local dev test1')
3876 print('### ip route show table local dev test1')
3877 print(output)
3878 self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
3879 self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3880 self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3881 self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
3882 self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3883 self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3884 output = check_output('ip -6 route show dev test1')
3885 print('### ip -6 route show dev test1')
3886 print(output)
3887 self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3888 self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3889 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3890 output = check_output('ip -6 route show table local dev test1')
3891 print('### ip -6 route show table local dev test1')
3892 print(output)
3893 self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3894 self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3895 self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3896 self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
3897 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3898
3899 def test_configure_without_carrier(self):
3900 copy_network_unit('11-dummy.netdev')
3901 start_networkd()
3902 self.wait_operstate('test1', 'off', '')
3903 check_output('ip link set dev test1 up carrier off')
3904
3905 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False)
3906 restart_networkd()
3907 self.wait_online('test1:no-carrier')
3908
3909 carrier_map = {'on': '1', 'off': '0'}
3910 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3911 for carrier in ['off', 'on', 'off']:
3912 with self.subTest(carrier=carrier):
3913 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3914 check_output(f'ip link set dev test1 carrier {carrier}')
3915 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3916
3917 output = networkctl_status('test1')
3918 print(output)
3919 self.assertRegex(output, '192.168.0.15')
3920 self.assertRegex(output, '192.168.0.1')
3921 self.assertRegex(output, routable_map[carrier])
3922
3923 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
3924 copy_network_unit('11-dummy.netdev')
3925 start_networkd()
3926 self.wait_operstate('test1', 'off', '')
3927 check_output('ip link set dev test1 up carrier off')
3928
3929 copy_network_unit('25-test1.network')
3930 restart_networkd()
3931 self.wait_online('test1:no-carrier')
3932
3933 carrier_map = {'on': '1', 'off': '0'}
3934 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3935 for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
3936 with self.subTest(carrier=carrier, have_config=have_config):
3937 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3938 check_output(f'ip link set dev test1 carrier {carrier}')
3939 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3940
3941 output = networkctl_status('test1')
3942 print(output)
3943 if have_config:
3944 self.assertRegex(output, '192.168.0.15')
3945 self.assertRegex(output, '192.168.0.1')
3946 else:
3947 self.assertNotRegex(output, '192.168.0.15')
3948 self.assertNotRegex(output, '192.168.0.1')
3949 self.assertRegex(output, routable_map[carrier])
3950
3951 def check_routing_policy_rule_test1(self):
3952 print('### Checking routing policy rules requested by test1')
3953
3954 output = check_output('ip rule list iif test1 priority 111')
3955 print(output)
3956 self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3957
3958 output = check_output('ip -6 rule list iif test1 priority 100')
3959 print(output)
3960 self.assertIn('100: from all iif test1 lookup 8', output)
3961
3962 output = check_output('ip rule list iif test1 priority 101')
3963 print(output)
3964 self.assertIn('101: from all iif test1 lookup 9', output)
3965
3966 output = check_output('ip -6 rule list iif test1 priority 101')
3967 print(output)
3968 self.assertIn('101: from all iif test1 lookup 9', output)
3969
3970 output = check_output('ip rule list iif test1 priority 102')
3971 print(output)
3972 self.assertIn('102: from 0.0.0.0/8 iif test1 lookup 10', output)
3973
3974 output = check_output('ip rule list iif test1 priority 103')
3975 print(output)
3976 self.assertIn('103: from 10.0.0.0/16 iif test1 lookup 11 goto 111', output)
3977
3978 output = check_output('ip rule list iif test1 priority 104')
3979 print(output)
3980 self.assertIn('104: from 10.1.0.0/16 iif test1 lookup 12 nop', output)
3981
3982 output = check_output('ip rule list iif test1 priority 200')
3983 print(output)
3984 self.assertIn('200: from all fwmark 0/0x1 iif test1 lookup 20', output)
3985
3986 output = check_output('ip rule list iif test1 priority 201')
3987 print(output)
3988 self.assertIn('201: from all fwmark 0x7/0xff iif test1 lookup 21', output)
3989
3990 output = check_output('ip rule list iif test1 priority 202')
3991 print(output)
3992 self.assertIn('202: from all fwmark 0x270f iif test1 lookup 22', output)
3993
3994 output = check_output('ip rule list to 192.0.2.0/26')
3995 print(output)
3996 self.assertIn('to 192.0.2.0/26 lookup 1001', output)
3997
3998 output = check_output('ip rule list to 192.0.2.64/26')
3999 print(output)
4000 self.assertIn('to 192.0.2.64/26 lookup 1001', output)
4001
4002 output = check_output('ip rule list to 192.0.2.128/26')
4003 print(output)
4004 self.assertIn('to 192.0.2.128/26 lookup 1001', output)
4005
4006 output = check_output('ip rule list to 192.0.2.192/26')
4007 print(output)
4008 self.assertIn('to 192.0.2.192/26 lookup 1001', output)
4009
4010 def check_routing_policy_rule_dummy98(self):
4011 print('### Checking routing policy rules requested by dummy98')
4012
4013 output = check_output('ip rule list priority 112')
4014 print(output)
4015 self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
4016
4017 def _test_routing_policy_rule(self, manage_foreign_routes):
4018 if not manage_foreign_routes:
4019 copy_networkd_conf_dropin('networkd-manage-foreign-rules-no.conf')
4020 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
4021
4022 stop_networkd()
4023
4024 check_output('ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16')
4025 check_output('ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64')
4026
4027 start_networkd()
4028 self.wait_online('test1:degraded')
4029
4030 self.check_routing_policy_rule_test1()
4031 check_json(networkctl_json())
4032
4033 output = check_output('ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16')
4034 print(output)
4035 if manage_foreign_routes:
4036 self.assertEqual(output, '')
4037 else:
4038 self.assertIn(output, '20001: from 10.10.0.0/16 lookup 9999')
4039 check_output('ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16')
4040
4041 output = check_output('ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64')
4042 print(output)
4043 if manage_foreign_routes:
4044 self.assertEqual(output, '')
4045 else:
4046 self.assertIn(output, '20001: from 2001:db8:0:1::/64 lookup 9999')
4047 check_output('ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64')
4048
4049 def test_routing_policy_rule(self):
4050 first = True
4051 for manage_foreign_routes in [True, False]:
4052 if first:
4053 first = False
4054 else:
4055 self.tearDown()
4056
4057 print(f'### test_routing_policy_rule(manage_foreign_routes={manage_foreign_routes})')
4058 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4059 self._test_routing_policy_rule(manage_foreign_routes)
4060
4061 def test_routing_policy_rule_restart_and_reconfigure(self):
4062 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
4063 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
4064
4065 # For #11280 and #34068.
4066
4067 for trial in range(3):
4068 restart_networkd(show_logs=(trial > 0))
4069 self.wait_online('test1:degraded', 'dummy98:degraded')
4070
4071 self.check_routing_policy_rule_test1()
4072 self.check_routing_policy_rule_dummy98()
4073
4074 networkctl_reconfigure('test1')
4075 self.wait_online('test1:degraded')
4076
4077 self.check_routing_policy_rule_test1()
4078 self.check_routing_policy_rule_dummy98()
4079
4080 networkctl_reconfigure('dummy98')
4081 self.wait_online('dummy98:degraded')
4082
4083 self.check_routing_policy_rule_test1()
4084 self.check_routing_policy_rule_dummy98()
4085
4086 def test_routing_policy_rule_reconfigure(self):
4087 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
4088 start_networkd()
4089 self.wait_online('test1:degraded')
4090
4091 output = check_output('ip rule list table 1011')
4092 print(output)
4093 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4094 self.assertIn('10112: from all oif test1 lookup 1011', output)
4095 self.assertIn('10113: from all iif test1 lookup 1011', output)
4096 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4097
4098 output = check_output('ip -6 rule list table 1011')
4099 print(output)
4100 self.assertIn('10112: from all oif test1 lookup 1011', output)
4101
4102 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
4103 networkctl_reload()
4104 self.wait_online('test1:degraded')
4105
4106 output = check_output('ip rule list table 1011')
4107 print(output)
4108 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4109 self.assertIn('10112: from all oif test1 lookup 1011', output)
4110 self.assertIn('10113: from all iif test1 lookup 1011', output)
4111 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4112
4113 output = check_output('ip -6 rule list table 1011')
4114 print(output)
4115 self.assertNotIn('10112: from all oif test1 lookup 1011', output)
4116 self.assertIn('10113: from all iif test1 lookup 1011', output)
4117
4118 call('ip rule delete priority 10111')
4119 call('ip rule delete priority 10112')
4120 call('ip rule delete priority 10113')
4121 call('ip rule delete priority 10114')
4122 call('ip -6 rule delete priority 10113')
4123
4124 output = check_output('ip rule list table 1011')
4125 print(output)
4126 self.assertEqual(output, '')
4127
4128 output = check_output('ip -6 rule list table 1011')
4129 print(output)
4130 self.assertEqual(output, '')
4131
4132 networkctl_reconfigure('test1')
4133 self.wait_online('test1:degraded')
4134
4135 output = check_output('ip rule list table 1011')
4136 print(output)
4137 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4138 self.assertIn('10112: from all oif test1 lookup 1011', output)
4139 self.assertIn('10113: from all iif test1 lookup 1011', output)
4140 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4141
4142 output = check_output('ip -6 rule list table 1011')
4143 print(output)
4144 self.assertIn('10113: from all iif test1 lookup 1011', output)
4145
4146 def test_routing_policy_rule_manual(self):
4147 # For issue #36244.
4148 copy_network_unit(
4149 '11-dummy.netdev',
4150 '25-routing-policy-rule-manual.network')
4151 start_networkd()
4152 self.wait_operstate('test1', operstate='off', setup_state='configuring', setup_timeout=20)
4153
4154 check_output('ip link add test2 type dummy')
4155 self.wait_operstate('test2', operstate='off', setup_state='configuring', setup_timeout=20)
4156
4157 networkctl('up', 'test2')
4158 self.wait_online('test2:degraded')
4159
4160 # The request for the routing policy rules are bound to test1. Hence, we need to wait for the rules
4161 # being configured explicitly.
4162 for _ in range(20):
4163 time.sleep(0.5)
4164
4165 output = check_output('ip -4 rule list table 51819')
4166 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4167 continue
4168
4169 output = check_output('ip -6 rule list table 51819')
4170 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4171 continue
4172
4173 output = check_output('ip -4 rule list table 51820')
4174 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4175 continue
4176
4177 output = check_output('ip -6 rule list table 51820')
4178 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4179 continue
4180
4181 break
4182 else:
4183 self.assertFalse(True)
4184
4185 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
4186 def test_routing_policy_rule_port_range(self):
4187 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
4188 start_networkd()
4189 self.wait_online('test1:degraded')
4190
4191 output = check_output('ip rule')
4192 print(output)
4193 self.assertIn('111:', output)
4194 self.assertIn('from 192.168.100.18 ', output)
4195 self.assertIn('sport 1123-1150 ', output)
4196 self.assertIn('dport 3224-3290 ', output)
4197 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4198 self.assertIn('lookup 7 ', output)
4199
4200 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
4201 def test_routing_policy_rule_invert(self):
4202 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
4203 start_networkd()
4204 self.wait_online('test1:degraded')
4205
4206 output = check_output('ip rule')
4207 print(output)
4208 self.assertIn('111:', output)
4209 self.assertIn('not ', output)
4210 self.assertIn('from 192.168.100.18 ', output)
4211 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4212 self.assertIn('lookup 7 ', output)
4213
4214 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
4215 def test_routing_policy_rule_l3mdev(self):
4216 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
4217 start_networkd()
4218 self.wait_online('test1:degraded')
4219
4220 output = check_output('ip rule')
4221 print(output)
4222 self.assertIn('1500: from all lookup [l3mdev-table]', output)
4223 self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
4224
4225 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
4226 def test_routing_policy_rule_uidrange(self):
4227 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
4228 start_networkd()
4229 self.wait_online('test1:degraded')
4230
4231 output = check_output('ip rule')
4232 print(output)
4233 self.assertIn('111:', output)
4234 self.assertIn('from 192.168.100.18 ', output)
4235 self.assertIn('lookup 7 ', output)
4236 self.assertIn('uidrange 100-200 ', output)
4237
4238 def _check_route_static(self, test1_is_managed: bool):
4239 output = networkctl_status('dummy98')
4240 print(output)
4241 output = networkctl_status('test1')
4242 print(output)
4243
4244 print('### ip -6 route show dev dummy98')
4245 output = check_output('ip -6 route show dev dummy98')
4246 print(output)
4247 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4248 self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
4249 self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
4250
4251 print('### ip -6 route show default')
4252 output = check_output('ip -6 route show default')
4253 print(output)
4254 self.assertIn('default', output)
4255 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output)
4256
4257 print('### ip -4 route show dev dummy98')
4258 output = check_output('ip -4 route show dev dummy98')
4259 print(output)
4260 self.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output)
4261 self.assertIn('149.10.124.64 proto static scope link', output)
4262 self.assertIn('169.254.0.0/16 proto static scope link metric 2048', output)
4263 self.assertIn('192.168.1.1 proto static scope link initcwnd 20', output)
4264 self.assertIn('192.168.1.2 proto static scope link initrwnd 30', output)
4265 self.assertIn('192.168.1.3 proto static scope link advmss 30', output)
4266 self.assertIn('192.168.1.4 proto static scope link hoplimit 122', output)
4267 self.assertIn('multicast 149.10.123.4 proto static', output)
4268
4269 print('### ip -4 route show dev dummy98 default')
4270 output = check_output('ip -4 route show dev dummy98 default')
4271 print(output)
4272 self.assertIn('default via 149.10.125.65 proto static onlink', output)
4273 self.assertIn('default via 149.10.124.64 proto static', output)
4274 self.assertIn('default proto static', output)
4275 self.assertIn('default via 1.1.8.104 proto static', output)
4276
4277 print('### ip -4 route show table local dev dummy98')
4278 output = check_output('ip -4 route show table local dev dummy98')
4279 print(output)
4280 self.assertIn('local 149.10.123.1 proto static scope host', output)
4281 self.assertIn('anycast 149.10.123.2 proto static scope link', output)
4282 self.assertIn('broadcast 149.10.123.3 proto static scope link', output)
4283
4284 print('### ip -4 route show type blackhole')
4285 output = check_output('ip -4 route show type blackhole')
4286 print(output)
4287 self.assertIn('blackhole 202.54.1.2 proto static', output)
4288
4289 print('### ip -4 route show type unreachable')
4290 output = check_output('ip -4 route show type unreachable')
4291 print(output)
4292 self.assertIn('unreachable 202.54.1.3 proto static', output)
4293
4294 print('### ip -4 route show type prohibit')
4295 output = check_output('ip -4 route show type prohibit')
4296 print(output)
4297 self.assertIn('prohibit 202.54.1.4 proto static', output)
4298
4299 print('### ip -6 route show type blackhole')
4300 output = check_output('ip -6 route show type blackhole')
4301 print(output)
4302 self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
4303
4304 print('### ip -6 route show type unreachable')
4305 output = check_output('ip -6 route show type unreachable')
4306 print(output)
4307 self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
4308
4309 print('### ip -6 route show type prohibit')
4310 output = check_output('ip -6 route show type prohibit')
4311 print(output)
4312 self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
4313
4314 print('### ip route show 192.168.10.1')
4315 output = check_output('ip route show 192.168.10.1')
4316 print(output)
4317 self.assertIn('192.168.10.1 proto static', output)
4318 self.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output)
4319 self.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output)
4320 self.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output)
4321 self.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output)
4322
4323 print('### ip route show 192.168.10.2')
4324 output = check_output('ip route show 192.168.10.2')
4325 print(output)
4326 # old ip command does not show IPv6 gateways...
4327 self.assertIn('192.168.10.2 proto static', output)
4328 self.assertIn('nexthop', output)
4329 self.assertIn('dev test1 weight 20', output)
4330 self.assertIn('dev test1 weight 30', output)
4331 self.assertIn('dev dummy98 weight 10', output)
4332 self.assertIn('dev dummy98 weight 5', output)
4333
4334 print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
4335 output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
4336 print(output)
4337 # old ip command does not show 'nexthop' keyword and weight...
4338 self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output)
4339 if test1_is_managed:
4340 self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output)
4341 self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output)
4342 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
4343 self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
4344
4345 check_json(networkctl_json())
4346
4347 def _check_unreachable_routes_removed(self):
4348 print('### ip -4 route show type blackhole')
4349 output = check_output('ip -4 route show type blackhole')
4350 print(output)
4351 self.assertEqual(output, '')
4352
4353 print('### ip -4 route show type unreachable')
4354 output = check_output('ip -4 route show type unreachable')
4355 print(output)
4356 self.assertEqual(output, '')
4357
4358 print('### ip -4 route show type prohibit')
4359 output = check_output('ip -4 route show type prohibit')
4360 print(output)
4361 self.assertEqual(output, '')
4362
4363 print('### ip -6 route show type blackhole')
4364 output = check_output('ip -6 route show type blackhole')
4365 print(output)
4366 self.assertEqual(output, '')
4367
4368 print('### ip -6 route show type unreachable')
4369 output = check_output('ip -6 route show type unreachable')
4370 print(output)
4371 self.assertEqual(output, '')
4372
4373 print('### ip -6 route show type prohibit')
4374 output = check_output('ip -6 route show type prohibit')
4375 print(output)
4376 self.assertEqual(output, '')
4377
4378 check_json(networkctl_json())
4379
4380 def _test_route_static(self, manage_foreign_routes):
4381 if not manage_foreign_routes:
4382 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
4383
4384 copy_network_unit('25-route-static.network', '12-dummy.netdev',
4385 '25-route-static-test1.network', '11-dummy.netdev')
4386 start_networkd()
4387 self.wait_online('dummy98:routable', 'test1:routable')
4388 self._check_route_static(test1_is_managed=True)
4389
4390 # unmanaging test1
4391 remove_network_unit('25-route-static-test1.network')
4392 networkctl_reload()
4393 self.wait_online('dummy98:routable')
4394 self.wait_operstate('test1', setup_state='unmanaged')
4395 self._check_route_static(test1_is_managed=False)
4396
4397 # managing test1 again
4398 copy_network_unit('25-route-static-test1.network')
4399 networkctl_reload()
4400 self.wait_online('dummy98:routable', 'test1:routable')
4401 self._check_route_static(test1_is_managed=True)
4402
4403 # adding an unmanaged interface
4404 check_output('ip link add test2 type dummy')
4405 self.wait_operstate('test2', operstate='off', setup_state='unmanaged')
4406 self._check_route_static(test1_is_managed=True)
4407
4408 # reconfiguring with another config, and check all routes managed by Manager are removed
4409 copy_network_unit('25-address-static.network', copy_dropins=False)
4410 networkctl_reload()
4411 self.wait_online('dummy98:routable')
4412 self._check_unreachable_routes_removed()
4413
4414 # reconfiguring the original config again
4415 remove_network_unit('25-address-static.network')
4416 networkctl_reload()
4417 self.wait_online('dummy98:routable')
4418 self._check_route_static(test1_is_managed=True)
4419
4420 # removing interface, and check all routes managed by Manager are removed
4421 remove_link('dummy98')
4422 time.sleep(2)
4423 self._check_unreachable_routes_removed()
4424
4425 def test_route_static(self):
4426 first = True
4427 for manage_foreign_routes in [True, False]:
4428 if first:
4429 first = False
4430 else:
4431 self.tearDown()
4432
4433 print(f'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
4434 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4435 self._test_route_static(manage_foreign_routes)
4436
4437 def test_route_static_issue_35047(self):
4438 copy_network_unit(
4439 '25-route-static-issue-35047.network',
4440 '25-route-static-issue-35047.network.d/step1.conf',
4441 '12-dummy.netdev',
4442 copy_dropins=False)
4443 start_networkd()
4444 self.wait_online('dummy98:routable')
4445
4446 print('### ip -4 route show table all dev dummy98')
4447 output = check_output('ip -4 route show table all dev dummy98')
4448 print(output)
4449 self.assertIn('192.0.2.2 proto kernel scope link src 192.0.2.1', output)
4450 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4451 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4452
4453 check_output('ip link set dev dummy98 down')
4454 self.wait_route_dropped('dummy98', '192.0.2.2 proto kernel scope link src 192.0.2.1', ipv='-4', table='all', timeout_sec=10)
4455 self.wait_route_dropped('dummy98', 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', ipv='-4', table='all', timeout_sec=10)
4456 self.wait_route_dropped('dummy98', '198.51.100.0/24 via 192.0.2.2 proto static', ipv='-4', table='all', timeout_sec=10)
4457
4458 print('### ip -4 route show table all dev dummy98')
4459 output = check_output('ip -4 route show table all dev dummy98')
4460 print(output)
4461 self.assertNotIn('192.0.2.2', output)
4462 self.assertNotIn('192.0.2.1', output)
4463 self.assertNotIn('198.51.100.0/24', output)
4464
4465 remove_network_unit('25-route-static-issue-35047.network.d/step1.conf')
4466 copy_network_unit('25-route-static-issue-35047.network.d/step2.conf')
4467 networkctl_reload()
4468 self.wait_online('dummy98:routable')
4469
4470 print('### ip -4 route show table all dev dummy98')
4471 output = check_output('ip -4 route show table all dev dummy98')
4472 print(output)
4473 self.assertIn('192.0.2.2 proto static scope link', output)
4474 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4475 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4476
4477 def test_route_static_issue_37714(self):
4478 copy_network_unit('12-dummy.netdev', '25-route-static-issue-37714.network')
4479 start_networkd()
4480 self.wait_online('dummy98:routable')
4481
4482 print('### ip -4 rule list table 249')
4483 output = check_output('ip -4 rule list table 249')
4484 print(output)
4485 self.assertIn('32765: from 192.168.0.227 lookup 249 proto static', output)
4486
4487 print('### ip -6 rule list table 249')
4488 output = check_output('ip -6 rule list table 249')
4489 print(output)
4490 self.assertIn('32765: from 2000:f00::227 lookup 249 proto static', output)
4491
4492 print('### ip -4 route show table all dev dummy98')
4493 output = check_output('ip -4 route show table all dev dummy98')
4494 print(output)
4495 self.assertIn('default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink', output)
4496 self.assertIn('192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128', output)
4497 self.assertIn('10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4498 self.assertIn('192.168.0.72 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4499 self.assertIn('192.168.0.193 proto static scope link src 192.168.0.227 metric 128', output)
4500 self.assertIn('local 192.168.0.227 table local proto kernel scope host src 192.168.0.227', output)
4501 self.assertIn('broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227', output)
4502
4503 print('### ip -6 route show table all dev dummy98')
4504 output = check_output('ip -6 route show table all dev dummy98')
4505 print(output)
4506 self.assertIn('2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium', output)
4507 self.assertIn('default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium', output)
4508 self.assertIn('fe80::/64 proto kernel metric 256 pref medium', output)
4509 self.assertIn('local 2000:f00::227 table local proto kernel metric 0 pref medium', output)
4510 self.assertRegex(output, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium', output)
4511 self.assertIn('multicast ff00::/8 table local proto kernel metric 256 pref medium', output)
4512
4513 @expectedFailureIfRTA_VIAIsNotSupported()
4514 def test_route_via_ipv6(self):
4515 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
4516 start_networkd()
4517 self.wait_online('dummy98:routable')
4518
4519 output = networkctl_status('dummy98')
4520 print(output)
4521
4522 print('### ip -6 route show dev dummy98')
4523 output = check_output('ip -6 route show dev dummy98')
4524 print(output)
4525 self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
4526 self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
4527
4528 print('### ip -4 route show dev dummy98')
4529 output = check_output('ip -4 route show dev dummy98')
4530 print(output)
4531 self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
4532 self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
4533
4534 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
4535 def test_route_congctl(self):
4536 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
4537 start_networkd()
4538 self.wait_online('dummy98:routable')
4539
4540 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4541 output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4542 print(output)
4543 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4544 self.assertIn('congctl dctcp', output)
4545
4546 print('### ip -4 route show dev dummy98 149.10.124.66')
4547 output = check_output('ip -4 route show dev dummy98 149.10.124.66')
4548 print(output)
4549 self.assertIn('149.10.124.66 proto static', output)
4550 self.assertIn('congctl dctcp', output)
4551 self.assertIn('rto_min 300s', output)
4552
4553 @expectedFailureIfModuleIsNotAvailable('vrf')
4554 def test_route_vrf(self):
4555 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
4556 '25-vrf.netdev', '25-vrf.network')
4557 start_networkd()
4558 self.wait_online('dummy98:routable', 'vrf99:carrier')
4559
4560 output = check_output('ip route show vrf vrf99')
4561 print(output)
4562 self.assertRegex(output, 'default via 192.168.100.1')
4563
4564 output = check_output('ip route show')
4565 print(output)
4566 self.assertNotRegex(output, 'default via 192.168.100.1')
4567
4568 def test_gateway_reconfigure(self):
4569 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
4570 start_networkd()
4571 self.wait_online('dummy98:routable')
4572 print('### ip -4 route show dev dummy98 default')
4573 output = check_output('ip -4 route show dev dummy98 default')
4574 print(output)
4575 self.assertIn('default via 149.10.124.59 proto static', output)
4576 self.assertNotIn('149.10.124.60', output)
4577
4578 remove_network_unit('25-gateway-static.network')
4579 copy_network_unit('25-gateway-next-static.network')
4580 networkctl_reload()
4581 self.wait_online('dummy98:routable')
4582 print('### ip -4 route show dev dummy98 default')
4583 output = check_output('ip -4 route show dev dummy98 default')
4584 print(output)
4585 self.assertNotIn('149.10.124.59', output)
4586 self.assertIn('default via 149.10.124.60 proto static', output)
4587
4588 def test_ip_route_ipv6_src_route(self):
4589 # a dummy device does not make the addresses go through tentative state, so we
4590 # reuse a bond from an earlier test, which does make the addresses go through
4591 # tentative state, and do our test on that
4592 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4593 start_networkd()
4594 self.wait_online('dummy98:enslaved', 'bond199:routable')
4595
4596 output = check_output('ip -6 route list dev bond199')
4597 print(output)
4598 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output)
4599
4600 def test_route_preferred_source_with_existing_address(self):
4601 # See issue #28009.
4602 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
4603 start_networkd()
4604
4605 for i in range(3):
4606 if i != 0:
4607 networkctl_reconfigure('dummy98')
4608
4609 self.wait_online('dummy98:routable')
4610
4611 output = check_output('ip -6 route list dev dummy98')
4612 print(output)
4613 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output)
4614
4615 output = check_output('ip -4 route list dev dummy98')
4616 print(output)
4617 self.assertIn('10.123.0.0/16 via 192.168.30.1 proto static src 10.10.10.1', output)
4618
4619 def test_ip_link_mac_address(self):
4620 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
4621 start_networkd()
4622 self.wait_online('dummy98:degraded')
4623
4624 output = check_output('ip link show dummy98')
4625 print(output)
4626 self.assertRegex(output, '00:01:02:aa:bb:cc')
4627
4628 def test_ip_link_unmanaged(self):
4629 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
4630 start_networkd()
4631
4632 self.wait_operstate('dummy98', 'off', setup_state='unmanaged')
4633
4634 def test_ipv6_address_label(self):
4635 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
4636 copy_networkd_conf_dropin('networkd-address-label.conf')
4637 start_networkd()
4638 self.wait_online('dummy98:degraded')
4639
4640 output = check_output('ip addrlabel list')
4641 print(output)
4642 self.assertRegex(output, '2004:da8:1::/64 dev dummy98 label 4444')
4643 self.assertRegex(output, '2004:da8:2::/64 label 5555')
4644
4645 def test_ipv6_proxy_ndp(self):
4646 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
4647 start_networkd()
4648
4649 self.wait_online('dummy98:routable')
4650
4651 output = check_output('ip neighbor show proxy dev dummy98')
4652 print(output)
4653 for i in range(1, 5):
4654 self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
4655
4656 def test_ipv6_neigh_retrans_time(self):
4657 link='test25'
4658 copy_network_unit('25-dummy.netdev', '25-dummy.network')
4659 start_networkd()
4660
4661 self.wait_online(f'{link}:degraded')
4662 remove_network_unit('25-dummy.network')
4663
4664 # expect retrans_time_ms updated
4665 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4666 networkctl_reload()
4667 self.wait_online(f'{link}:degraded')
4668 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4669 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4670
4671 # expect retrans_time_ms unchanged
4672 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4673 networkctl_reload()
4674 self.wait_online(f'{link}:degraded')
4675 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4676 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4677
4678 # expect retrans_time_ms unchanged
4679 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4680 networkctl_reload()
4681 self.wait_online(f'{link}:degraded')
4682 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4683 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4684
4685 # expect retrans_time_ms unchanged
4686 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4687 networkctl_reload()
4688 self.wait_online(f'{link}:degraded')
4689 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4690 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4691
4692 # expect retrans_time_ms unchanged
4693 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4694 networkctl_reload()
4695 self.wait_online(f'{link}:degraded')
4696 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4697 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4698
4699 # expect retrans_time_ms updated
4700 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4701 networkctl_reload()
4702 self.wait_online(f'{link}:degraded')
4703 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000')
4704 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4705
4706 def test_neighbor(self):
4707 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
4708 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
4709 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
4710 copy_dropins=False)
4711 start_networkd()
4712 self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
4713
4714 print('### ip neigh list dev gretun97')
4715 output = check_output('ip neigh list dev gretun97')
4716 print(output)
4717 self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
4718 self.assertNotIn('10.0.0.23', output)
4719
4720 print('### ip neigh list dev ip6gretun97')
4721 output = check_output('ip neigh list dev ip6gretun97')
4722 print(output)
4723 self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
4724 self.assertNotIn('2001:db8:0:f102::18', output)
4725
4726 print('### ip neigh list dev dummy98')
4727 output = check_output('ip neigh list dev dummy98')
4728 print(output)
4729 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
4730 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
4731 self.assertNotIn('2004:da8:1:0::2', output)
4732 self.assertNotIn('192.168.10.2', output)
4733 self.assertNotIn('00:00:5e:00:02:67', output)
4734
4735 check_json(networkctl_json())
4736
4737 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
4738 # the valid configurations in 10-step1.conf.
4739 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
4740 networkctl_reload()
4741 self.wait_online('dummy98:degraded')
4742
4743 print('### ip neigh list dev dummy98')
4744 output = check_output('ip neigh list dev dummy98')
4745 print(output)
4746 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
4747 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
4748 self.assertNotIn('2004:da8:1:0::2', output)
4749 self.assertNotIn('192.168.10.2', output)
4750 self.assertNotIn('00:00:5e:00:02:67', output)
4751
4752 check_json(networkctl_json())
4753
4754 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
4755 '25-neighbor-dummy.network.d/10-step2.conf')
4756 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
4757 networkctl_reload()
4758 self.wait_online('dummy98:degraded')
4759
4760 print('### ip neigh list dev dummy98')
4761 output = check_output('ip neigh list dev dummy98')
4762 print(output)
4763 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
4764 self.assertNotIn('00:00:5e:00:02:65', output)
4765 self.assertNotIn('00:00:5e:00:02:66', output)
4766 self.assertNotIn('00:00:5e:00:03:65', output)
4767 self.assertNotIn('2004:da8:1::1', output)
4768
4769 def test_link_local_addressing(self):
4770 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
4771 '25-link-local-addressing-no.network', '12-dummy.netdev')
4772 start_networkd()
4773 self.wait_online('test1:degraded', 'dummy98:carrier')
4774
4775 output = check_output('ip address show dev test1')
4776 print(output)
4777 self.assertRegex(output, 'inet .* scope link')
4778 self.assertRegex(output, 'inet6 .* scope link')
4779
4780 output = check_output('ip address show dev dummy98')
4781 print(output)
4782 self.assertNotRegex(output, 'inet6* .* scope link')
4783
4784 # Documentation/networking/ip-sysctl.txt
4785 #
4786 # addr_gen_mode - INTEGER
4787 # Defines how link-local and autoconf addresses are generated.
4788 #
4789 # 0: generate address based on EUI64 (default)
4790 # 1: do no generate a link-local address, use EUI64 for addresses generated
4791 # from autoconf
4792 # 2: generate stable privacy addresses, using the secret from
4793 # stable_secret (RFC7217)
4794 # 3: generate stable privacy addresses, using a random secret if unset
4795
4796 self.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
4797 self.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
4798 self.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
4799
4800 def test_link_local_addressing_ipv6ll(self):
4801 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
4802 start_networkd()
4803 self.wait_online('dummy98:degraded')
4804
4805 # An IPv6LL address exists by default.
4806 output = check_output('ip address show dev dummy98')
4807 print(output)
4808 self.assertRegex(output, 'inet6 .* scope link')
4809
4810 copy_network_unit('25-link-local-addressing-no.network')
4811 networkctl_reload()
4812 self.wait_online('dummy98:carrier')
4813
4814 # Check if the IPv6LL address is removed.
4815 output = check_output('ip address show dev dummy98')
4816 print(output)
4817 self.assertNotRegex(output, 'inet6 .* scope link')
4818
4819 remove_network_unit('25-link-local-addressing-no.network')
4820 networkctl_reload()
4821 self.wait_online('dummy98:degraded')
4822
4823 # Check if a new IPv6LL address is assigned.
4824 output = check_output('ip address show dev dummy98')
4825 print(output)
4826 self.assertRegex(output, 'inet6 .* scope link')
4827
4828 def test_sysctl(self):
4829 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
4830 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
4831 start_networkd()
4832 self.wait_online('dummy98:degraded')
4833
4834 self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
4835 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
4836 self.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
4837 self.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
4838 self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
4839 self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
4840 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
4841 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
4842 self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
4843 self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
4844 self.check_ipv4_sysctl_attr('dummy98', 'force_igmp_version', '1')
4845
4846 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
4847 networkctl_reload()
4848 self.wait_online('dummy98:degraded')
4849
4850 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
4851
4852 def test_sysctl_disable_ipv6(self):
4853 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
4854
4855 print('## Disable ipv6')
4856 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
4857 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
4858
4859 start_networkd()
4860 self.wait_online('dummy98:routable')
4861
4862 output = check_output('ip -4 address show dummy98')
4863 print(output)
4864 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
4865 output = check_output('ip -6 address show dummy98')
4866 print(output)
4867 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4868 self.assertRegex(output, 'inet6 .* scope link')
4869 output = check_output('ip -4 route show dev dummy98')
4870 print(output)
4871 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
4872 output = check_output('ip -6 route show default')
4873 print(output)
4874 self.assertRegex(output, 'default')
4875 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4876
4877 remove_link('dummy98')
4878
4879 print('## Enable ipv6')
4880 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
4881 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
4882
4883 restart_networkd()
4884 self.wait_online('dummy98:routable')
4885
4886 output = check_output('ip -4 address show dummy98')
4887 print(output)
4888 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
4889 output = check_output('ip -6 address show dummy98')
4890 print(output)
4891 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4892 self.assertRegex(output, 'inet6 .* scope link')
4893 output = check_output('ip -4 route show dev dummy98')
4894 print(output)
4895 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
4896 output = check_output('ip -6 route show default')
4897 print(output)
4898 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4899
4900 @expectedFailureIfModuleIsNotAvailable('mpls_router')
4901 def test_sysctl_mpls(self):
4902 check_output('modprobe mpls_router')
4903 copy_network_unit('25-sysctl-mpls.network', '12-dummy.netdev')
4904 start_networkd()
4905 self.wait_online('dummy98:degraded')
4906
4907 self.check_mpls_sysctl_attr('dummy98', 'input', '1')
4908
4909 def test_bind_carrier(self):
4910 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
4911 start_networkd()
4912
4913 # no bound interface.
4914 self.wait_operstate('test1', 'off', setup_state='configuring')
4915 output = check_output('ip address show test1')
4916 print(output)
4917 self.assertNotIn('UP,LOWER_UP', output)
4918 self.assertIn('DOWN', output)
4919 self.assertNotIn('192.168.10', output)
4920
4921 # add one bound interface. The interface will be up.
4922 check_output('ip link add dummy98 type dummy')
4923 check_output('ip link set dummy98 up')
4924 self.wait_online('test1:routable')
4925 output = check_output('ip address show test1')
4926 print(output)
4927 self.assertIn('UP,LOWER_UP', output)
4928 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4929
4930 # add another bound interface. The interface is still up.
4931 check_output('ip link add dummy99 type dummy')
4932 check_output('ip link set dummy99 up')
4933 self.wait_operstate('dummy99', 'degraded', setup_state='unmanaged')
4934 output = check_output('ip address show test1')
4935 print(output)
4936 self.assertIn('UP,LOWER_UP', output)
4937 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4938
4939 # remove one of the bound interfaces. The interface is still up
4940 remove_link('dummy98')
4941 output = check_output('ip address show test1')
4942 print(output)
4943 self.assertIn('UP,LOWER_UP', output)
4944 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4945
4946 # bring down the remaining bound interface. The interface will be down.
4947 check_output('ip link set dummy99 down')
4948 self.wait_operstate('test1', 'off')
4949 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4950 output = check_output('ip address show test1')
4951 print(output)
4952 self.assertNotIn('UP,LOWER_UP', output)
4953 self.assertIn('DOWN', output)
4954 self.assertNotIn('192.168.10', output)
4955
4956 # bring up the bound interface. The interface will be up.
4957 check_output('ip link set dummy99 up')
4958 self.wait_online('test1:routable')
4959 output = check_output('ip address show test1')
4960 print(output)
4961 self.assertIn('UP,LOWER_UP', output)
4962 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4963
4964 # remove the remaining bound interface. The interface will be down.
4965 remove_link('dummy99')
4966 self.wait_operstate('test1', 'off')
4967 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4968 output = check_output('ip address show test1')
4969 print(output)
4970 self.assertNotIn('UP,LOWER_UP', output)
4971 self.assertIn('DOWN', output)
4972 self.assertNotIn('192.168.10', output)
4973
4974 # re-add one bound interface. The interface will be up.
4975 check_output('ip link add dummy98 type dummy')
4976 check_output('ip link set dummy98 up')
4977 self.wait_online('test1:routable')
4978 output = check_output('ip address show test1')
4979 print(output)
4980 self.assertIn('UP,LOWER_UP', output)
4981 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4982
4983 def _test_activation_policy(self, interface, test):
4984 conffile = '25-activation-policy.network'
4985 if test:
4986 conffile = f'{conffile}.d/{test}.conf'
4987 if interface == 'vlan99':
4988 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
4989 copy_network_unit('11-dummy.netdev', conffile, copy_dropins=False)
4990 start_networkd()
4991
4992 always = test.startswith('always')
4993 initial_up = test != 'manual' and not test.endswith('down') # note: default is up
4994 expect_up = initial_up
4995 next_up = not expect_up
4996
4997 if test.endswith('down'):
4998 self.wait_activated(interface)
4999
5000 for iteration in range(4):
5001 with self.subTest(iteration=iteration, expect_up=expect_up):
5002 operstate = 'routable' if expect_up else 'off'
5003 setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None)
5004 self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20)
5005
5006 if expect_up:
5007 self.assertIn('UP', check_output(f'ip link show {interface}'))
5008 self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}'))
5009 self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}'))
5010 else:
5011 self.assertIn('DOWN', check_output(f'ip link show {interface}'))
5012
5013 if next_up:
5014 check_output(f'ip link set dev {interface} up')
5015 else:
5016 check_output(f'ip link set dev {interface} down')
5017 expect_up = initial_up if always else next_up
5018 next_up = not next_up
5019 if always:
5020 time.sleep(1)
5021
5022 def test_activation_policy(self):
5023 first = True
5024 for interface in ['test1', 'vlan99']:
5025 for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
5026 if first:
5027 first = False
5028 else:
5029 self.tearDown()
5030
5031 print(f'### test_activation_policy(interface={interface}, test={test})')
5032 with self.subTest(interface=interface, test=test):
5033 self._test_activation_policy(interface, test)
5034
5035 def _test_activation_policy_required_for_online(self, policy, required):
5036 conffile = '25-activation-policy.network'
5037 units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
5038 if policy:
5039 units += [f'{conffile}.d/{policy}.conf']
5040 if required:
5041 units += [f'{conffile}.d/required-{required}.conf']
5042 copy_network_unit(*units, copy_dropins=False)
5043 start_networkd()
5044
5045 if policy.endswith('down'):
5046 self.wait_activated('test1')
5047
5048 if policy.endswith('down') or policy == 'manual':
5049 self.wait_operstate('test1', 'off', setup_state='configuring')
5050 else:
5051 self.wait_online('test1')
5052
5053 if policy == 'always-down':
5054 # if always-down, required for online is forced to no
5055 expected = False
5056 elif required:
5057 # otherwise if required for online is specified, it should match that
5058 expected = required == 'yes'
5059 elif policy:
5060 # otherwise if only policy specified, required for online defaults to
5061 # true if policy is up, always-up, or bound
5062 expected = policy.endswith('up') or policy == 'bound'
5063 else:
5064 # default is true, if neither are specified
5065 expected = True
5066
5067 output = networkctl_status('test1')
5068 print(output)
5069
5070 yesno = 'yes' if expected else 'no'
5071 self.assertRegex(output, f'Required For Online: {yesno}')
5072
5073 def test_activation_policy_required_for_online(self):
5074 first = True
5075 for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
5076 for required in ['yes', 'no', '']:
5077 if first:
5078 first = False
5079 else:
5080 self.tearDown()
5081
5082 print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})')
5083 with self.subTest(policy=policy, required=required):
5084 self._test_activation_policy_required_for_online(policy, required)
5085
5086 def test_domain(self):
5087 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
5088 start_networkd()
5089 self.wait_online('dummy98:routable')
5090
5091 output = networkctl_status('dummy98')
5092 print(output)
5093 self.assertRegex(output, 'Address: 192.168.42.100')
5094 self.assertRegex(output, 'DNS: 192.168.42.1')
5095 self.assertRegex(output, 'Search Domains: one')
5096
5097 def test_keep_configuration_yes(self):
5098 check_output('ip link add dummy98 type dummy')
5099 check_output('ip link set dev dummy98 up')
5100 check_output('ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98')
5101 check_output('ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot')
5102
5103 copy_network_unit('24-keep-configuration-yes.network')
5104 start_networkd()
5105 self.wait_online('dummy98:routable')
5106
5107 output = check_output('ip address show dummy98')
5108 print(output)
5109 self.assertIn('inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98', output)
5110
5111 output = check_output('ip -d -4 route show dev dummy98')
5112 print(output)
5113 self.assertIn('203.0.113.0/24 via 198.51.100.10 proto boot', output)
5114
5115 def test_keep_configuration_static(self):
5116 check_output('ip link add name dummy98 type dummy')
5117 check_output('ip address add 10.1.2.3/16 dev dummy98')
5118 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
5119 output = check_output('ip address show dummy98')
5120 print(output)
5121 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5122 self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5123 output = check_output('ip route show dev dummy98')
5124 print(output)
5125
5126 copy_network_unit('24-keep-configuration-static.network')
5127 start_networkd()
5128 self.wait_online('dummy98:routable')
5129
5130 output = check_output('ip address show dummy98')
5131 print(output)
5132 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5133 self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5134
5135 def check_keep_configuration_on_restart(self):
5136 self.wait_online('dummy98:routable')
5137 self.wait_online('unmanaged0:routable', setup_state='unmanaged')
5138
5139 print('### ip -6 address show dev dummy98')
5140 output = check_output('ip -6 address show dev dummy98')
5141 print(output)
5142 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
5143 self.assertIn('inet6 2001:db8:1:f101::15/64 scope global deprecated', output)
5144
5145 print('### ip -6 address show dev unmanaged0')
5146 output = check_output('ip -6 address show dev unmanaged0')
5147 print(output)
5148 self.assertIn('inet6 2001:db8:9999:f101::15/64 scope global', output)
5149
5150 print('### ip -6 route show default dev dummy98')
5151 output = check_output('ip -6 route show default dev dummy98')
5152 print(output)
5153 self.assertIn('default via fe80::f0ca:cc1a proto static metric 1 pref medium', output)
5154
5155 def test_keep_configuration_on_restart(self):
5156 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network', '85-unmanaged.link')
5157
5158 # Add an unmanaged interface with an up address
5159 call('ip link add unmanaged0 type dummy')
5160 call('ip link set unmanaged0 up')
5161 call('ip -4 addr add 10.20.30.40/32 dev unmanaged0')
5162 call('ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0')
5163
5164 # Wait for all addresses
5165 self.wait_address('unmanaged0', 'inet 10.20.30.40/32', scope='global', ipv='-4', timeout_sec=10)
5166 self.wait_address('unmanaged0', 'inet6 2001:db8:9999:f101::15/64', scope='global', ipv='-6', timeout_sec=10)
5167 self.wait_address('unmanaged0', 'inet6 fe80::[0-9a-f:]*/64', scope='link', ipv='-6', timeout_sec=10)
5168
5169 # Wait for all routes
5170 self.wait_route('unmanaged0', 'local 10.20.30.40 proto kernel', table='local', ipv='-4', timeout_sec=10)
5171 self.wait_route('unmanaged0', 'local fe80::[0-9a-f:]* proto kernel', table='local', ipv='-6', timeout_sec=10)
5172 self.wait_route('unmanaged0', 'multicast ff00::/8 proto kernel', table='local', ipv='-6', timeout_sec=10)
5173 self.wait_route('unmanaged0', '2001:db8:9999:f101::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5174 self.wait_route('unmanaged0', 'fe80::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5175
5176 # Start `ip monitor` with output to a temporary file
5177 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor_u') as logfile_unmanaged:
5178 process_u = subprocess.Popen(['ip', 'monitor', 'dev', 'unmanaged0'], stdout=logfile_unmanaged, text=True)
5179
5180 start_networkd()
5181 self.check_keep_configuration_on_restart()
5182
5183 # Start `ip monitor` with output to a temporary file
5184 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor') as logfile:
5185 process = subprocess.Popen(['ip', 'monitor', 'dev', 'dummy98'], stdout=logfile, text=True)
5186
5187 restart_networkd()
5188 self.check_keep_configuration_on_restart()
5189
5190 process.send_signal(signal.SIGTERM)
5191 process.wait()
5192
5193 print('### ip monitor dev dummy98 BEGIN')
5194
5195 # Read the `ip monitor` output looking for network changes
5196 logfile.seek(0)
5197 for line in logfile:
5198 line = line.rstrip()
5199 print(line)
5200 # Check if a link went down
5201 self.assertNotRegex(line, 'dummy98: .* state DOWN')
5202 # Check if an address was removed
5203 self.assertNotRegex(line, '^Deleted .* 2001:db8:')
5204 self.assertNotRegex(line, '^Deleted 2001:db8:.*/64')
5205 # Check if the default route was removed
5206 self.assertNotRegex(line, '^Deleted default via fe80::f0ca:cc1a')
5207
5208 print('### ip monitor dev dummy98 END')
5209
5210 process_u.send_signal(signal.SIGTERM)
5211 process_u.wait()
5212
5213 print('### ip monitor dev unmanaged0 BEGIN')
5214
5215 # Read the `ip monitor` output looking for network changes and check if something happened
5216 logfile_unmanaged.seek(0)
5217 self.assertEqual(logfile_unmanaged.read().rstrip(), '')
5218
5219 print('### ip monitor dev unmanaged0 END')
5220
5221 def test_keep_untracked_addresses(self):
5222 # Add an unmanaged interface with an up address
5223
5224 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network')
5225 start_networkd()
5226 self.wait_online('dummy98:routable')
5227
5228 print('### ip -4 addr add 10.234.77.111/32 dev dummy98')
5229 output = check_output('ip -4 addr add 10.234.77.111/32 dev dummy98')
5230 print(output)
5231
5232 print('### ip -6 addr add 2222:3333::4444/64 dev dummy98')
5233 output = check_output('ip -6 addr add 2222:3333::4444/64 dev dummy98')
5234 print(output)
5235
5236 restart_networkd()
5237
5238 output = check_output('ip -4 addr show dev dummy98')
5239 print(output)
5240 self.assertIn('inet 10.234.77.111/32', output)
5241
5242 output = check_output('ip -6 addr show dev dummy98')
5243 print(output)
5244 self.assertIn('inet6 2222:3333::4444/64 scope global', output)
5245
5246 def check_nexthop(self, manage_foreign_nexthops, first):
5247 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
5248
5249 output = check_output('ip nexthop list dev veth99')
5250 print(output)
5251 if first:
5252 self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
5253 self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
5254 else:
5255 self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
5256 self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output)
5257 self.assertIn('id 3 dev veth99', output)
5258 self.assertIn('id 4 dev veth99', output)
5259 if first:
5260 self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
5261 else:
5262 self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
5263 self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
5264 self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
5265 if manage_foreign_nexthops:
5266 self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
5267
5268 output = check_output('ip nexthop list dev dummy98')
5269 print(output)
5270 if first:
5271 self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
5272 else:
5273 self.assertIn('id 21 via 192.168.20.1 dev dummy98', output)
5274 if manage_foreign_nexthops:
5275 self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
5276 else:
5277 self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
5278
5279 # kernel manages blackhole nexthops on lo
5280 output = check_output('ip nexthop list dev lo')
5281 print(output)
5282 if first:
5283 self.assertIn('id 6 blackhole', output)
5284 self.assertIn('id 7 blackhole', output)
5285 else:
5286 self.assertIn('id 1 blackhole', output)
5287 self.assertIn('id 2 blackhole', output)
5288
5289 # group nexthops are shown with -0 option
5290 if first:
5291 output = check_output('ip -0 nexthop list id 21')
5292 print(output)
5293 self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
5294 else:
5295 output = check_output('ip -0 nexthop list id 20')
5296 print(output)
5297 self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)')
5298
5299 output = check_output('ip route show dev veth99 10.10.10.10')
5300 print(output)
5301 if first:
5302 self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
5303 else:
5304 self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output)
5305
5306 output = check_output('ip route show dev veth99 10.10.10.11')
5307 print(output)
5308 if first:
5309 self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
5310 else:
5311 self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output)
5312
5313 output = check_output('ip route show dev veth99 10.10.10.12')
5314 print(output)
5315 if first:
5316 self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
5317 else:
5318 self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output)
5319
5320 output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
5321 print(output)
5322 if first:
5323 self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
5324 else:
5325 self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
5326
5327 output = check_output('ip route show 10.10.10.13')
5328 print(output)
5329 if first:
5330 self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
5331 else:
5332 self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output)
5333
5334 output = check_output('ip -6 route show 2001:1234:5:8f62::2')
5335 print(output)
5336 if first:
5337 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
5338 else:
5339 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output)
5340
5341 output = check_output('ip route show 10.10.10.14')
5342 print(output)
5343 if first:
5344 self.assertIn('10.10.10.14 nhid 21 proto static', output)
5345 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5346 else:
5347 self.assertIn('10.10.10.14 nhid 20 proto static', output)
5348 self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output)
5349 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
5350
5351 output = networkctl_json()
5352 check_json(output)
5353 self.assertNotIn('"Destination":[10.10.10.14]', output)
5354
5355 def _test_nexthop(self, manage_foreign_nexthops):
5356 if not manage_foreign_nexthops:
5357 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
5358
5359 check_output('ip link add dummy98 type dummy')
5360 check_output('ip link set dummy98 up')
5361 check_output('ip address add 192.168.20.20/24 dev dummy98')
5362 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
5363
5364 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
5365 '12-dummy.netdev', '25-nexthop-dummy-1.network')
5366 start_networkd()
5367
5368 self.check_nexthop(manage_foreign_nexthops, first=True)
5369
5370 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
5371 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
5372 networkctl_reload()
5373 self.check_nexthop(manage_foreign_nexthops, first=False)
5374
5375 remove_network_unit('25-nexthop-2.network')
5376 copy_network_unit('25-nexthop-nothing.network')
5377 networkctl_reload()
5378 self.wait_online('veth99:routable', 'veth-peer:routable')
5379
5380 output = check_output('ip nexthop list dev veth99')
5381 print(output)
5382 self.assertEqual(output, '')
5383 output = check_output('ip nexthop list dev lo')
5384 print(output)
5385 self.assertEqual(output, '')
5386
5387 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
5388 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
5389 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
5390 # here to test reconfiguring with different .network files does not trigger race.
5391 # See also comments in link_drop_requests().
5392 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
5393 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
5394
5395 self.check_nexthop(manage_foreign_nexthops, first=True)
5396
5397 # Remove nexthop with ID 20
5398 check_output('ip nexthop del id 20')
5399
5400 # Check the nexthop ID 20 is dropped from the group nexthop.
5401 output = check_output('ip -0 nexthop list id 21')
5402 print(output)
5403 self.assertRegex(output, r'id 21 group 1,3')
5404
5405 # Remove nexthop with ID 21
5406 check_output('ip nexthop del id 21')
5407 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
5408 networkctl_reload()
5409
5410 # 25-nexthop-test1.network requests a route with nexthop ID 21, which is removed in the above,
5411 # hence test1 should be stuck in the configuring state.
5412 self.wait_operstate('test1', operstate='routable', setup_state='configuring')
5413
5414 # Wait for a while, and check if the interface is still in the configuring state.
5415 time.sleep(1)
5416 output = networkctl_status('test1')
5417 self.assertIn('State: routable (configuring)', output)
5418
5419 # Check if the route which needs nexthop 21 are forgotten.
5420 output = networkctl_json()
5421 check_json(output)
5422 self.assertNotIn('"Destination":[10.10.10.14]', output)
5423
5424 # Reconfigure the interface that has nexthop with ID 20 and 21,
5425 # then the route requested by test1 can be configured.
5426 networkctl_reconfigure('dummy98')
5427 self.wait_online('test1:routable')
5428
5429 # Check if the requested route actually configured.
5430 output = check_output('ip route show 10.10.11.10')
5431 print(output)
5432 self.assertIn('10.10.11.10 nhid 21 proto static', output)
5433 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5434 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
5435
5436 remove_link('veth99')
5437 time.sleep(2)
5438
5439 output = check_output('ip nexthop list dev lo')
5440 print(output)
5441 self.assertEqual(output, '')
5442
5443 @expectedFailureIfNexthopIsNotAvailable()
5444 def test_nexthop(self):
5445 first = True
5446 for manage_foreign_nexthops in [True, False]:
5447 if first:
5448 first = False
5449 else:
5450 self.tearDown()
5451
5452 print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
5453 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
5454 self._test_nexthop(manage_foreign_nexthops)
5455
5456 class NetworkdTCTests(unittest.TestCase, Utilities):
5457
5458 def setUp(self):
5459 setup_common()
5460
5461 def tearDown(self):
5462 tear_down_common()
5463
5464 @expectedFailureIfModuleIsNotAvailable('sch_cake')
5465 def test_qdisc_cake(self):
5466 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
5467 start_networkd()
5468 self.wait_online('dummy98:routable')
5469
5470 output = check_output('tc qdisc show dev dummy98')
5471 print(output)
5472 self.assertIn('qdisc cake 3a: root', output)
5473 self.assertIn('bandwidth 500Mbit', output)
5474 self.assertIn('autorate-ingress', output)
5475 self.assertIn('diffserv8', output)
5476 self.assertIn('dual-dsthost', output)
5477 self.assertIn(' nat', output)
5478 self.assertIn(' wash', output)
5479 self.assertIn(' split-gso', output)
5480 self.assertIn(' raw', output)
5481 self.assertIn(' atm', output)
5482 self.assertIn('overhead 128', output)
5483 self.assertIn('mpu 20', output)
5484 self.assertIn('fwmark 0xff00', output)
5485 self.assertIn('rtt 1s', output)
5486 self.assertIn('ack-filter-aggressive', output)
5487
5488 # Test for replacing existing qdisc. See #31226.
5489 with open(os.path.join(network_unit_dir, '25-qdisc-cake.network'), mode='a', encoding='utf-8') as f:
5490 f.write('Bandwidth=250M\n')
5491
5492 networkctl_reload()
5493 self.wait_online('dummy98:routable')
5494
5495 output = check_output('tc qdisc show dev dummy98')
5496 print(output)
5497 self.assertIn('bandwidth 250Mbit', output)
5498
5499 @expectedFailureIfModuleIsNotAvailable('sch_codel')
5500 def test_qdisc_codel(self):
5501 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
5502 start_networkd()
5503 self.wait_online('dummy98:routable')
5504
5505 output = check_output('tc qdisc show dev dummy98')
5506 print(output)
5507 self.assertRegex(output, 'qdisc codel 33: root')
5508 self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
5509
5510 @expectedFailureIfModuleIsNotAvailable('sch_drr')
5511 def test_qdisc_drr(self):
5512 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
5513 start_networkd()
5514 self.wait_online('dummy98:routable')
5515
5516 output = check_output('tc qdisc show dev dummy98')
5517 print(output)
5518 self.assertRegex(output, 'qdisc drr 2: root')
5519 output = check_output('tc class show dev dummy98')
5520 print(output)
5521 self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
5522
5523 @expectedFailureIfModuleIsNotAvailable('sch_ets')
5524 def test_qdisc_ets(self):
5525 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
5526 start_networkd()
5527 self.wait_online('dummy98:routable')
5528
5529 output = check_output('tc qdisc show dev dummy98')
5530 print(output)
5531
5532 self.assertRegex(output, 'qdisc ets 3a: root')
5533 self.assertRegex(output, 'bands 10 strict 3')
5534 self.assertRegex(output, 'quanta 1 2 3 4 5')
5535 self.assertRegex(output, 'priomap 3 4 5 6 7')
5536
5537 @expectedFailureIfModuleIsNotAvailable('sch_fq')
5538 def test_qdisc_fq(self):
5539 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
5540 start_networkd()
5541 self.wait_online('dummy98:routable')
5542
5543 output = check_output('tc qdisc show dev dummy98')
5544 print(output)
5545 self.assertRegex(output, 'qdisc fq 32: root')
5546 self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
5547 self.assertRegex(output, 'quantum 1500')
5548 self.assertRegex(output, 'initial_quantum 13000')
5549 self.assertRegex(output, 'maxrate 1Mbit')
5550
5551 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
5552 def test_qdisc_fq_codel(self):
5553 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
5554 start_networkd()
5555 self.wait_online('dummy98:routable')
5556
5557 output = check_output('tc qdisc show dev dummy98')
5558 print(output)
5559 self.assertRegex(output, 'qdisc fq_codel 34: root')
5560 self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
5561
5562 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
5563 def test_qdisc_fq_pie(self):
5564 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
5565 start_networkd()
5566 self.wait_online('dummy98:routable')
5567
5568 output = check_output('tc qdisc show dev dummy98')
5569 print(output)
5570
5571 self.assertRegex(output, 'qdisc fq_pie 3a: root')
5572 self.assertRegex(output, 'limit 200000p')
5573
5574 @expectedFailureIfModuleIsNotAvailable('sch_gred')
5575 def test_qdisc_gred(self):
5576 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
5577 start_networkd()
5578 self.wait_online('dummy98:routable')
5579
5580 output = check_output('tc qdisc show dev dummy98')
5581 print(output)
5582 self.assertRegex(output, 'qdisc gred 38: root')
5583 self.assertRegex(output, 'vqs 12 default 10 grio')
5584
5585 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
5586 def test_qdisc_hhf(self):
5587 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
5588 start_networkd()
5589 self.wait_online('dummy98:routable')
5590
5591 output = check_output('tc qdisc show dev dummy98')
5592 print(output)
5593 self.assertRegex(output, 'qdisc hhf 3a: root')
5594 self.assertRegex(output, 'limit 1022p')
5595
5596 @expectedFailureIfModuleIsNotAvailable('sch_htb')
5597 def test_qdisc_htb_fifo(self):
5598 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
5599 start_networkd()
5600 self.wait_online('dummy98:routable')
5601
5602 output = check_output('tc qdisc show dev dummy98')
5603 print(output)
5604 self.assertRegex(output, 'qdisc htb 2: root')
5605 self.assertRegex(output, r'default (0x30|30)')
5606
5607 self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
5608 self.assertRegex(output, 'limit 100000p')
5609
5610 self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
5611 self.assertRegex(output, 'limit 1000000')
5612
5613 self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
5614 self.assertRegex(output, 'limit 1023p')
5615
5616 self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
5617
5618 output = check_output('tc -d class show dev dummy98')
5619 print(output)
5620 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
5621 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
5622 # which is fixed in v6.3.0 by
5623 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
5624 self.assertRegex(output, 'class htb 2:37 root leaf 37(:|prio) ')
5625 self.assertRegex(output, 'class htb 2:3a root leaf 3a(:|prio) ')
5626 self.assertRegex(output, 'class htb 2:3b root leaf 3b(:|prio) ')
5627 self.assertRegex(output, 'class htb 2:3c root leaf 3c(:|prio) ')
5628 self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
5629 self.assertRegex(output, 'burst 123456')
5630 self.assertRegex(output, 'cburst 123457')
5631
5632 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
5633 def test_qdisc_ingress(self):
5634 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
5635 '25-qdisc-ingress.network', '11-dummy.netdev')
5636 start_networkd()
5637 self.wait_online('dummy98:routable', 'test1:routable')
5638
5639 output = check_output('tc qdisc show dev dummy98')
5640 print(output)
5641 self.assertRegex(output, 'qdisc clsact')
5642
5643 output = check_output('tc qdisc show dev test1')
5644 print(output)
5645 self.assertRegex(output, 'qdisc ingress')
5646
5647 def test_qdisc_mq(self):
5648 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-mq.network')
5649 start_networkd()
5650 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5651
5652 output = check_output('tc qdisc show dev testtun99')
5653 print(output)
5654 self.assertIn('qdisc mq 2: root', output)
5655
5656 @expectedFailureIfModuleIsNotAvailable('sch_multiq')
5657 def test_qdisc_multiq(self):
5658 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-multiq.network')
5659 start_networkd()
5660 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5661
5662 output = check_output('tc qdisc show dev testtun99')
5663 print(output)
5664 self.assertIn('qdisc multiq 2: root', output)
5665
5666 @expectedFailureIfModuleIsNotAvailable('sch_netem')
5667 def test_qdisc_netem(self):
5668 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
5669 '25-qdisc-netem-compat.network', '11-dummy.netdev')
5670 start_networkd()
5671 self.wait_online('dummy98:routable', 'test1:routable')
5672
5673 output = check_output('tc qdisc show dev dummy98')
5674 print(output)
5675 self.assertRegex(output, 'qdisc netem 30: root')
5676 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
5677
5678 output = check_output('tc qdisc show dev test1')
5679 print(output)
5680 self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
5681 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
5682
5683 @expectedFailureIfModuleIsNotAvailable('sch_pie')
5684 def test_qdisc_pie(self):
5685 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
5686 start_networkd()
5687 self.wait_online('dummy98:routable')
5688
5689 output = check_output('tc qdisc show dev dummy98')
5690 print(output)
5691 self.assertRegex(output, 'qdisc pie 3a: root')
5692 self.assertRegex(output, 'limit 200000')
5693
5694 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
5695 def test_qdisc_qfq(self):
5696 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
5697 start_networkd()
5698 self.wait_online('dummy98:routable')
5699
5700 output = check_output('tc qdisc show dev dummy98')
5701 print(output)
5702 self.assertRegex(output, 'qdisc qfq 2: root')
5703 output = check_output('tc class show dev dummy98')
5704 print(output)
5705 self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
5706 self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
5707
5708 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
5709 def test_qdisc_sfb(self):
5710 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
5711 start_networkd()
5712 self.wait_online('dummy98:routable')
5713
5714 output = check_output('tc qdisc show dev dummy98')
5715 print(output)
5716 self.assertRegex(output, 'qdisc sfb 39: root')
5717 self.assertRegex(output, 'limit 200000')
5718
5719 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
5720 def test_qdisc_sfq(self):
5721 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
5722 start_networkd()
5723 self.wait_online('dummy98:routable')
5724
5725 output = check_output('tc qdisc show dev dummy98')
5726 print(output)
5727 self.assertRegex(output, 'qdisc sfq 36: root')
5728 self.assertRegex(output, 'perturb 5sec')
5729
5730 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
5731 def test_qdisc_tbf(self):
5732 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
5733 start_networkd()
5734 self.wait_online('dummy98:routable')
5735
5736 output = check_output('tc qdisc show dev dummy98')
5737 print(output)
5738 self.assertRegex(output, 'qdisc tbf 35: root')
5739 self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms')
5740
5741 @expectedFailureIfModuleIsNotAvailable('sch_teql')
5742 def test_qdisc_teql(self):
5743 call_quiet('rmmod sch_teql')
5744
5745 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
5746 start_networkd()
5747 self.wait_links('dummy98')
5748 check_output('modprobe sch_teql max_equalizers=2')
5749 self.wait_online('dummy98:routable')
5750
5751 output = check_output('tc qdisc show dev dummy98')
5752 print(output)
5753 self.assertRegex(output, 'qdisc teql1 31: root')
5754
5755 @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
5756 def test_qdisc_drop(self):
5757 copy_network_unit('12-dummy.netdev', '12-dummy.network')
5758 start_networkd()
5759 self.wait_online('dummy98:routable')
5760
5761 # Test case for issue #32247 and #32254.
5762 for _ in range(20):
5763 check_output('tc qdisc replace dev dummy98 root fq')
5764 self.assertFalse(networkd_is_failed())
5765 check_output('tc qdisc replace dev dummy98 root fq pacing')
5766 self.assertFalse(networkd_is_failed())
5767 check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
5768 self.assertFalse(networkd_is_failed())
5769 check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
5770 self.assertFalse(networkd_is_failed())
5771
5772 class NetworkdStateFileTests(unittest.TestCase, Utilities):
5773
5774 def setUp(self):
5775 setup_common()
5776
5777 def tearDown(self):
5778 tear_down_common()
5779
5780 def test_state_file(self):
5781 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
5782 start_networkd()
5783 self.wait_online('dummy98:routable')
5784
5785 # make link state file updated
5786 resolvectl('revert', 'dummy98')
5787
5788 check_json(networkctl_json())
5789
5790 output = read_link_state_file('dummy98')
5791 print(output)
5792 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5793 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5794 self.assertIn('ADMIN_STATE=configured', output)
5795 self.assertIn('OPER_STATE=routable', output)
5796 self.assertIn('REQUIRED_FOR_ONLINE=yes', output)
5797 self.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output)
5798 self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output)
5799 self.assertIn('ACTIVATION_POLICY=up', output)
5800 self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output)
5801 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5802 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5803 self.assertIn('DOMAINS=hogehoge', output)
5804 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5805 self.assertIn('LLMNR=no', output)
5806 self.assertIn('MDNS=yes', output)
5807 self.assertIn('DNSSEC=no', output)
5808
5809 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
5810 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
5811 resolvectl('llmnr', 'dummy98', 'yes')
5812 resolvectl('mdns', 'dummy98', 'no')
5813 resolvectl('dnssec', 'dummy98', 'yes')
5814 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
5815
5816 check_json(networkctl_json())
5817
5818 output = read_link_state_file('dummy98')
5819 print(output)
5820 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5821 self.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output)
5822 self.assertIn('DOMAINS=hogehogehoge', output)
5823 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5824 self.assertIn('LLMNR=yes', output)
5825 self.assertIn('MDNS=no', output)
5826 self.assertIn('DNSSEC=yes', output)
5827
5828 timedatectl('revert', 'dummy98')
5829
5830 check_json(networkctl_json())
5831
5832 output = read_link_state_file('dummy98')
5833 print(output)
5834 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5835 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5836 self.assertIn('DOMAINS=hogehogehoge', output)
5837 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5838 self.assertIn('LLMNR=yes', output)
5839 self.assertIn('MDNS=no', output)
5840 self.assertIn('DNSSEC=yes', output)
5841
5842 resolvectl('revert', 'dummy98')
5843
5844 check_json(networkctl_json())
5845
5846 output = read_link_state_file('dummy98')
5847 print(output)
5848 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5849 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5850 self.assertIn('DOMAINS=hogehoge', output)
5851 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5852 self.assertIn('LLMNR=no', output)
5853 self.assertIn('MDNS=yes', output)
5854 self.assertIn('DNSSEC=no', output)
5855
5856 def test_address_state(self):
5857 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
5858 start_networkd()
5859
5860 self.wait_online('dummy98:degraded')
5861
5862 output = read_link_state_file('dummy98')
5863 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5864 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5865
5866 # with a routable IPv4 address
5867 check_output('ip address add 10.1.2.3/16 dev dummy98')
5868 self.wait_online('dummy98:routable', ipv4=True)
5869 self.wait_online('dummy98:routable')
5870
5871 output = read_link_state_file('dummy98')
5872 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5873 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5874
5875 check_output('ip address del 10.1.2.3/16 dev dummy98')
5876
5877 # with a routable IPv6 address
5878 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
5879 self.wait_online('dummy98:routable', ipv6=True)
5880 self.wait_online('dummy98:routable')
5881
5882 output = read_link_state_file('dummy98')
5883 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5884 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5885
5886 class NetworkdBondTests(unittest.TestCase, Utilities):
5887
5888 def setUp(self):
5889 setup_common()
5890
5891 def tearDown(self):
5892 tear_down_common()
5893
5894 def test_bond_keep_master(self):
5895 check_output('ip link add bond199 type bond mode active-backup')
5896 check_output('ip link add dummy98 type dummy')
5897 check_output('ip link set dummy98 master bond199')
5898
5899 copy_network_unit('23-keep-master.network')
5900 start_networkd()
5901 self.wait_online('dummy98:enslaved')
5902
5903 output = check_output('ip -d link show bond199')
5904 print(output)
5905 self.assertRegex(output, 'active_slave dummy98')
5906
5907 output = check_output('ip -d link show dummy98')
5908 print(output)
5909 self.assertRegex(output, 'master bond199')
5910
5911 # Test case for #37629
5912 for _ in range(3):
5913 # When a slave leaved from its master bonding interface, the kernel brings down the slave.
5914 check_output('ip link set dummy98 nomaster')
5915 self.wait_online('dummy98:off')
5916
5917 # Bring up the interface to check if networkd recognizes the interface has no master now.
5918 check_output('ip link set dummy98 up')
5919 self.wait_online('dummy98:carrier')
5920
5921 # We need to first bring down the interface to make it join a bonding interface.
5922 check_output('ip link set dummy98 down')
5923 check_output('ip link set dummy98 master bond199')
5924 self.wait_online('dummy98:enslaved')
5925
5926 def test_bond_active_slave(self):
5927 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
5928 start_networkd()
5929 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5930
5931 output = check_output('ip -d link show bond199')
5932 print(output)
5933 self.assertIn('active_slave dummy98', output)
5934
5935 # test case for issue #31165.
5936 since = datetime.datetime.now()
5937 networkctl_reconfigure('dummy98')
5938 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5939 self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
5940
5941 # test for reloading.
5942 touch_network_unit(
5943 '23-active-slave.network',
5944 '23-bond199.network',
5945 '25-bond-active-backup-slave.netdev',
5946 '12-dummy.netdev')
5947 networkctl_reload()
5948 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5949
5950 def test_bond_primary_slave(self):
5951 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
5952 start_networkd()
5953 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5954
5955 output = check_output('ip -d link show bond199')
5956 print(output)
5957 self.assertIn('primary dummy98', output)
5958
5959 # for issue #25627
5960 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
5961 for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
5962 with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
5963 f.write(f'[Link]\nMACAddress={mac}\n')
5964
5965 networkctl_reload()
5966 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5967
5968 output = check_output('ip -d link show bond199')
5969 print(output)
5970 self.assertIn(f'link/ether {mac}', output)
5971
5972 def test_bond_operstate(self):
5973 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
5974 '25-bond99.network', '25-bond-slave.network')
5975 start_networkd()
5976 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
5977
5978 output = check_output('ip -d link show dummy98')
5979 print(output)
5980 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
5981
5982 output = check_output('ip -d link show test1')
5983 print(output)
5984 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
5985
5986 output = check_output('ip -d link show bond99')
5987 print(output)
5988 self.assertRegex(output, 'MASTER,UP,LOWER_UP')
5989
5990 # test case for issue #32186
5991 restart_networkd()
5992 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
5993
5994 self.wait_operstate('dummy98', 'enslaved')
5995 self.wait_operstate('test1', 'enslaved')
5996 self.wait_operstate('bond99', 'routable')
5997
5998 check_output('ip link set dummy98 down')
5999
6000 self.wait_operstate('dummy98', 'off')
6001 self.wait_operstate('test1', 'enslaved')
6002 self.wait_operstate('bond99', 'routable')
6003
6004 check_output('ip link set dummy98 up')
6005
6006 self.wait_operstate('dummy98', 'enslaved')
6007 self.wait_operstate('test1', 'enslaved')
6008 self.wait_operstate('bond99', 'routable')
6009
6010 check_output('ip link set dummy98 down')
6011 check_output('ip link set test1 down')
6012
6013 self.wait_operstate('dummy98', 'off')
6014 self.wait_operstate('test1', 'off')
6015
6016 if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
6017 # Huh? Kernel does not recognize that all slave interfaces are down?
6018 # Let's confirm that networkd's operstate is consistent with ip's result.
6019 output = check_output('ip -d link show bond99')
6020 print(output)
6021 self.assertNotRegex(output, 'NO-CARRIER')
6022
6023 class NetworkdBridgeTests(unittest.TestCase, Utilities):
6024
6025 def setUp(self):
6026 setup_common()
6027
6028 def tearDown(self):
6029 tear_down_common()
6030
6031 def test_bridge_mac_none(self):
6032 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
6033 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
6034 start_networkd()
6035 self.wait_online('dummy98:enslaved', 'bridge99:degraded')
6036
6037 output = check_output('ip link show dev dummy98')
6038 print(output)
6039 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6040
6041 output = check_output('ip link show dev bridge99')
6042 print(output)
6043 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6044
6045 def test_bridge_vlan(self):
6046 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
6047 '26-bridge.netdev', '26-bridge-vlan-master.network',
6048 copy_dropins=False)
6049 start_networkd()
6050 self.wait_online('test1:enslaved', 'bridge99:degraded')
6051
6052 output = check_output('bridge vlan show dev test1')
6053 print(output)
6054 # check if the default VID is removed
6055 self.assertNotIn('1 Egress Untagged', output)
6056 for i in range(1000, 3000):
6057 if i == 1010:
6058 self.assertIn(f'{i} PVID', output)
6059 elif i in range(1012, 1016) or i in range(1103, 1109):
6060 self.assertIn(f'{i} Egress Untagged', output)
6061 elif i in range(1008, 1014) or i in range(1100, 1111):
6062 self.assertIn(f'{i}', output)
6063 else:
6064 self.assertNotIn(f'{i}', output)
6065
6066 output = check_output('bridge vlan show dev bridge99')
6067 print(output)
6068 # check if the default VID is removed
6069 self.assertNotIn('1 Egress Untagged', output)
6070 for i in range(1000, 3000):
6071 if i == 1020:
6072 self.assertIn(f'{i} PVID', output)
6073 elif i in range(1022, 1026) or i in range(1203, 1209):
6074 self.assertIn(f'{i} Egress Untagged', output)
6075 elif i in range(1018, 1024) or i in range(1200, 1211):
6076 self.assertIn(f'{i}', output)
6077 else:
6078 self.assertNotIn(f'{i}', output)
6079
6080 # Change vlan IDs
6081 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
6082 '26-bridge-vlan-master.network.d/10-override.conf')
6083 networkctl_reload()
6084 self.wait_online('test1:enslaved', 'bridge99:degraded')
6085
6086 output = check_output('bridge vlan show dev test1')
6087 print(output)
6088 for i in range(1000, 3000):
6089 if i == 2010:
6090 self.assertIn(f'{i} PVID', output)
6091 elif i in range(2012, 2016) or i in range(2103, 2109):
6092 self.assertIn(f'{i} Egress Untagged', output)
6093 elif i in range(2008, 2014) or i in range(2100, 2111):
6094 self.assertIn(f'{i}', output)
6095 else:
6096 self.assertNotIn(f'{i}', output)
6097
6098 output = check_output('bridge vlan show dev bridge99')
6099 print(output)
6100 for i in range(1000, 3000):
6101 if i == 2020:
6102 self.assertIn(f'{i} PVID', output)
6103 elif i in range(2022, 2026) or i in range(2203, 2209):
6104 self.assertIn(f'{i} Egress Untagged', output)
6105 elif i in range(2018, 2024) or i in range(2200, 2211):
6106 self.assertIn(f'{i}', output)
6107 else:
6108 self.assertNotIn(f'{i}', output)
6109
6110 # Remove several vlan IDs
6111 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
6112 '26-bridge-vlan-master.network.d/20-override.conf')
6113 networkctl_reload()
6114 self.wait_online('test1:enslaved', 'bridge99:degraded')
6115
6116 output = check_output('bridge vlan show dev test1')
6117 print(output)
6118 for i in range(1000, 3000):
6119 if i == 2010:
6120 self.assertIn(f'{i} PVID', output)
6121 elif i in range(2012, 2016):
6122 self.assertIn(f'{i} Egress Untagged', output)
6123 elif i in range(2008, 2014):
6124 self.assertIn(f'{i}', output)
6125 else:
6126 self.assertNotIn(f'{i}', output)
6127
6128 output = check_output('bridge vlan show dev bridge99')
6129 print(output)
6130 for i in range(1000, 3000):
6131 if i == 2020:
6132 self.assertIn(f'{i} PVID', output)
6133 elif i in range(2022, 2026):
6134 self.assertIn(f'{i} Egress Untagged', output)
6135 elif i in range(2018, 2024):
6136 self.assertIn(f'{i}', output)
6137 else:
6138 self.assertNotIn(f'{i}', output)
6139
6140 # Remove all vlan IDs
6141 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
6142 '26-bridge-vlan-master.network.d/30-override.conf')
6143 networkctl_reload()
6144 self.wait_online('test1:enslaved', 'bridge99:degraded')
6145
6146 output = check_output('bridge vlan show dev test1')
6147 print(output)
6148 self.assertNotIn('PVID', output)
6149 for i in range(1000, 3000):
6150 self.assertNotIn(f'{i}', output)
6151
6152 output = check_output('bridge vlan show dev bridge99')
6153 print(output)
6154 self.assertNotIn('PVID', output)
6155 for i in range(1000, 3000):
6156 self.assertNotIn(f'{i}', output)
6157
6158 def test_bridge_vlan_issue_20373(self):
6159 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
6160 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
6161 '21-vlan.netdev', '21-vlan.network')
6162 start_networkd()
6163 self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
6164
6165 output = check_output('bridge vlan show dev test1')
6166 print(output)
6167 self.assertIn('100 PVID Egress Untagged', output)
6168 self.assertIn('560', output)
6169 self.assertIn('600', output)
6170
6171 output = check_output('bridge vlan show dev bridge99')
6172 print(output)
6173 self.assertIn('1 PVID Egress Untagged', output)
6174 self.assertIn('100', output)
6175 self.assertIn('600', output)
6176
6177 def test_bridge_mdb(self):
6178 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
6179 '26-bridge.netdev', '26-bridge-mdb-master.network')
6180 start_networkd()
6181 self.wait_online('test1:enslaved', 'bridge99:degraded')
6182
6183 output = check_output('bridge mdb show dev bridge99')
6184 print(output)
6185 self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
6186 self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
6187
6188 # Old kernel may not support bridge MDB entries on bridge master
6189 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
6190 self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
6191 self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
6192
6193 # Old kernel may not support L2 bridge MDB entries
6194 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0:
6195 self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069')
6196
6197 def test_bridge_keep_master(self):
6198 check_output('ip link add bridge99 type bridge')
6199 check_output('ip link set bridge99 up')
6200 check_output('ip link add dummy98 type dummy')
6201 check_output('ip link set dummy98 master bridge99')
6202
6203 copy_network_unit('23-keep-master.network')
6204 start_networkd()
6205 self.wait_online('dummy98:enslaved')
6206
6207 output = check_output('ip -d link show dummy98')
6208 print(output)
6209 self.assertRegex(output, 'master bridge99')
6210 self.assertRegex(output, 'bridge')
6211
6212 output = check_output('bridge -d link show dummy98')
6213 print(output)
6214 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6215 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6216 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6217 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6218 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
6219 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6220 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6221 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6222 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6223 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
6224 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6225 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
6226
6227 def check_bridge_property(self):
6228 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
6229
6230 output = check_output('ip -d link show bridge99')
6231 print(output)
6232 self.assertIn('mtu 9000 ', output)
6233
6234 output = check_output('ip -d link show test1')
6235 print(output)
6236 self.assertIn('master bridge99 ', output)
6237 self.assertIn('bridge_slave', output)
6238 self.assertIn('mtu 9000 ', output)
6239
6240 output = check_output('ip -d link show dummy98')
6241 print(output)
6242 self.assertIn('master bridge99 ', output)
6243 self.assertIn('bridge_slave', output)
6244 self.assertIn('mtu 9000 ', output)
6245
6246 output = check_output('ip addr show bridge99')
6247 print(output)
6248 self.assertIn('192.168.0.15/24', output)
6249
6250 output = check_output('bridge -d link show dummy98')
6251 print(output)
6252 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6253 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6254 self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
6255 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6256 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6257 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
6258 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6259 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6260 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6261 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6262 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
6263 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6264 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
6265
6266 output = check_output('bridge -d link show test1')
6267 print(output)
6268 self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
6269 self.assertIn('locked on', output)
6270 if ' mab ' in output: # This is new in kernel and iproute2 v6.2
6271 self.assertIn('mab on', output)
6272
6273 def test_bridge_property(self):
6274 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6275 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
6276 '25-bridge99.network', '14-dummy.netdev', '26-bridge-vlan-tunnel.network')
6277 start_networkd()
6278 self.check_bridge_property()
6279
6280 # test reload
6281 touch_network_unit(
6282 '11-dummy.netdev',
6283 '12-dummy.netdev',
6284 '26-bridge.netdev',
6285 '26-bridge-slave-interface-1.network',
6286 '26-bridge-slave-interface-2.network',
6287 '26-bridge-vlan-tunnel.network',
6288 '25-bridge99.network')
6289 networkctl_reload()
6290 self.check_bridge_property()
6291
6292 check_output('ip address add 192.168.0.16/24 dev bridge99')
6293 output = check_output('ip addr show bridge99')
6294 print(output)
6295 self.assertIn('192.168.0.16/24', output)
6296
6297 # for issue #6088
6298 print('### ip -6 route list table all dev bridge99')
6299 output = check_output('ip -6 route list table all dev bridge99')
6300 print(output)
6301 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
6302
6303 remove_link('test1')
6304 self.wait_operstate('bridge99', 'routable')
6305
6306 output = check_output('ip -d link show bridge99')
6307 print(output)
6308 self.assertIn('mtu 9000 ', output)
6309
6310 output = check_output('ip -d link show dummy98')
6311 print(output)
6312 self.assertIn('master bridge99 ', output)
6313 self.assertIn('bridge_slave', output)
6314 self.assertIn('mtu 9000 ', output)
6315
6316 output = check_output('ip -d link show dummy97')
6317 self.assertIn('vlan_tunnel on ', output)
6318
6319 remove_link('dummy98')
6320 remove_link('dummy97')
6321 self.wait_operstate('bridge99', 'no-carrier')
6322
6323 output = check_output('ip -d link show bridge99')
6324 print(output)
6325 # When no carrier, the kernel may reset the MTU
6326 self.assertIn('NO-CARRIER', output)
6327
6328 output = check_output('ip address show bridge99')
6329 print(output)
6330 self.assertNotIn('192.168.0.15/24', output)
6331 self.assertIn('192.168.0.16/24', output) # foreign address is kept
6332
6333 print('### ip -6 route list table all dev bridge99')
6334 output = check_output('ip -6 route list table all dev bridge99')
6335 print(output)
6336 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
6337
6338 check_output('ip link add dummy98 type dummy')
6339 self.wait_online('dummy98:enslaved', 'bridge99:routable')
6340
6341 output = check_output('ip -d link show bridge99')
6342 print(output)
6343 self.assertIn('mtu 9000 ', output)
6344
6345 output = check_output('ip -d link show dummy98')
6346 print(output)
6347 self.assertIn('master bridge99 ', output)
6348 self.assertIn('bridge_slave', output)
6349 self.assertIn('mtu 9000 ', output)
6350
6351 def test_bridge_configure_without_carrier(self):
6352 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
6353 '11-dummy.netdev')
6354 start_networkd()
6355
6356 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
6357 for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
6358 with self.subTest(test=test):
6359 if test == 'no-slave':
6360 # bridge has no slaves; it's up but *might* not have carrier
6361 self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
6362 # due to a bug in the kernel, newly-created bridges are brought up
6363 # *with* carrier, unless they have had any setting changed; e.g.
6364 # their mac set, priority set, etc. Then, they will lose carrier
6365 # as soon as a (down) slave interface is added, and regain carrier
6366 # again once the slave interface is brought up.
6367 #self.check_link_attr('bridge99', 'carrier', '0')
6368 elif test == 'add-slave':
6369 # add slave to bridge, but leave it down; bridge is definitely no-carrier
6370 self.check_link_attr('test1', 'operstate', 'down')
6371 check_output('ip link set dev test1 master bridge99')
6372 self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
6373 self.check_link_attr('bridge99', 'carrier', '0')
6374 elif test == 'slave-up':
6375 # bring up slave, which will have carrier; bridge gains carrier
6376 check_output('ip link set dev test1 up')
6377 self.wait_online('bridge99:routable')
6378 self.check_link_attr('bridge99', 'carrier', '1')
6379 elif test == 'slave-no-carrier':
6380 # drop slave carrier; bridge loses carrier
6381 check_output('ip link set dev test1 carrier off')
6382 self.wait_online('bridge99:no-carrier:no-carrier')
6383 self.check_link_attr('bridge99', 'carrier', '0')
6384 elif test == 'slave-carrier':
6385 # restore slave carrier; bridge gains carrier
6386 check_output('ip link set dev test1 carrier on')
6387 self.wait_online('bridge99:routable')
6388 self.check_link_attr('bridge99', 'carrier', '1')
6389 elif test == 'slave-down':
6390 # bring down slave; bridge loses carrier
6391 check_output('ip link set dev test1 down')
6392 self.wait_online('bridge99:no-carrier:no-carrier')
6393 self.check_link_attr('bridge99', 'carrier', '0')
6394
6395 output = networkctl_status('bridge99')
6396 self.assertRegex(output, '10.1.2.3')
6397 self.assertRegex(output, '10.1.2.1')
6398
6399 def test_bridge_ignore_carrier_loss(self):
6400 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6401 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
6402 '25-bridge99-ignore-carrier-loss.network')
6403 start_networkd()
6404 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
6405
6406 check_output('ip address add 192.168.0.16/24 dev bridge99')
6407 remove_link('test1', 'dummy98')
6408 time.sleep(3)
6409
6410 output = check_output('ip address show bridge99')
6411 print(output)
6412 self.assertRegex(output, 'NO-CARRIER')
6413 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6414 self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
6415
6416 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
6417 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
6418 '25-bridge99-ignore-carrier-loss.network')
6419 start_networkd()
6420 self.wait_online('bridge99:no-carrier')
6421
6422 for trial in range(4):
6423 check_output('ip link add dummy98 type dummy')
6424 check_output('ip link set dummy98 up')
6425 if trial < 3:
6426 remove_link('dummy98')
6427
6428 self.wait_online('bridge99:routable', 'dummy98:enslaved')
6429
6430 output = check_output('ip address show bridge99')
6431 print(output)
6432 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6433
6434 output = check_output('ip rule list table 100')
6435 print(output)
6436 self.assertIn('from all to 8.8.8.8 lookup 100', output)
6437
6438 class NetworkdSRIOVTests(unittest.TestCase, Utilities):
6439
6440 def setUp(self):
6441 setup_common()
6442
6443 def tearDown(self):
6444 tear_down_common()
6445
6446 def setup_netdevsim(self, id=99, num_ports=1, num_vfs=0):
6447 call('modprobe netdevsim')
6448
6449 # Create netdevsim device.
6450 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
6451 f.write(f'{id} {num_ports}')
6452
6453 # Create VF.
6454 if num_vfs > 0:
6455 with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f:
6456 f.write(f'{num_vfs}')
6457
6458 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6459 def test_sriov(self):
6460 copy_network_unit('25-netdevsim.link', '25-sriov.network')
6461
6462 self.setup_netdevsim(num_vfs=3)
6463
6464 start_networkd()
6465 self.wait_online('sim99:routable')
6466
6467 output = check_output('ip link show dev sim99')
6468 print(output)
6469 self.assertRegex(output,
6470 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6471 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6472 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6473 )
6474
6475 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6476 def test_sriov_udev(self):
6477 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
6478
6479 self.setup_netdevsim()
6480
6481 start_networkd()
6482 self.wait_online('sim99:routable')
6483
6484 # The name sim99 is an alternative name, and cannot be used by udevadm below.
6485 ifname = link_resolve('sim99')
6486
6487 output = check_output('ip link show dev sim99')
6488 print(output)
6489 self.assertRegex(output,
6490 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6491 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6492 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6493 )
6494 self.assertNotIn('vf 3', output)
6495 self.assertNotIn('vf 4', output)
6496
6497 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6498 f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
6499
6500 udevadm_reload()
6501 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6502
6503 output = check_output('ip link show dev sim99')
6504 print(output)
6505 self.assertRegex(output,
6506 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6507 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6508 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6509 'vf 3'
6510 )
6511 self.assertNotIn('vf 4', output)
6512
6513 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6514 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6515
6516 udevadm_reload()
6517 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6518
6519 output = check_output('ip link show dev sim99')
6520 print(output)
6521 self.assertRegex(output,
6522 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6523 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6524 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6525 'vf 3'
6526 )
6527 self.assertNotIn('vf 4', output)
6528
6529 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6530 f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
6531
6532 udevadm_reload()
6533 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6534
6535 output = check_output('ip link show dev sim99')
6536 print(output)
6537 self.assertRegex(output,
6538 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6539 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
6540 )
6541 self.assertNotIn('vf 2', output)
6542 self.assertNotIn('vf 3', output)
6543 self.assertNotIn('vf 4', output)
6544
6545 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6546 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6547
6548 udevadm_reload()
6549 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6550
6551 output = check_output('ip link show dev sim99')
6552 print(output)
6553 self.assertRegex(output,
6554 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
6555 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6556 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6557 )
6558 self.assertNotIn('vf 3', output)
6559 self.assertNotIn('vf 4', output)
6560
6561 class NetworkdLLDPTests(unittest.TestCase, Utilities):
6562
6563 def setUp(self):
6564 setup_common()
6565
6566 def tearDown(self):
6567 tear_down_common()
6568
6569 def test_lldp(self):
6570 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
6571 start_networkd()
6572 self.wait_online('veth99:degraded', 'veth-peer:degraded')
6573
6574 for _ in range(20):
6575 output = networkctl('lldp')
6576 print(output)
6577 if re.search(r'veth99 .* veth-peer .* .......a...', output):
6578 break
6579 time.sleep(0.5)
6580 else:
6581 self.fail()
6582
6583 # With interface name
6584 output = networkctl('lldp', 'veth99');
6585 print(output)
6586 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
6587
6588 # With interface name pattern
6589 output = networkctl('lldp', 've*9');
6590 print(output)
6591 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
6592
6593 # json format
6594 output = networkctl('--json=short', 'lldp')
6595 print(output)
6596 self.assertIn('"InterfaceName":"veth99"', output)
6597 self.assertIn('"PortID":"veth-peer"', output)
6598 self.assertIn('"EnabledCapabilities":128', output)
6599
6600 # json format with interface name
6601 output = networkctl('--json=short', 'lldp', 'veth99')
6602 print(output)
6603 self.assertIn('"InterfaceName":"veth99"', output)
6604 self.assertIn('"PortID":"veth-peer"', output)
6605 self.assertIn('"EnabledCapabilities":128', output)
6606
6607 # json format with interface name pattern
6608 output = networkctl('--json=short', 'lldp', 've*9')
6609 print(output)
6610 self.assertIn('"InterfaceName":"veth99"', output)
6611 self.assertIn('"PortID":"veth-peer"', output)
6612 self.assertIn('"EnabledCapabilities":128', output)
6613
6614 # LLDP neighbors in status
6615 output = networkctl_status('veth99')
6616 print(output)
6617 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6618
6619 # enable forwarding, to enable the Router flag
6620 with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
6621 f.write('[Network]\nIPv4Forwarding=yes\n')
6622
6623 networkctl_reload()
6624 self.wait_online('veth-peer:degraded')
6625
6626 for _ in range(20):
6627 output = networkctl('lldp')
6628 print(output)
6629 if re.search(r'veth99 .* veth-peer .* ....r......', output):
6630 break
6631 time.sleep(0.5)
6632 else:
6633 self.fail()
6634
6635 # With interface name
6636 output = networkctl('lldp', 'veth99');
6637 print(output)
6638 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6639
6640 # With interface name pattern
6641 output = networkctl('lldp', 've*9');
6642 print(output)
6643 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6644
6645 # json format
6646 output = networkctl('--json=short', 'lldp')
6647 print(output)
6648 self.assertIn('"InterfaceName":"veth99"', output)
6649 self.assertIn('"PortID":"veth-peer"', output)
6650 self.assertIn('"EnabledCapabilities":16', output)
6651
6652 # json format with interface name
6653 output = networkctl('--json=short', 'lldp', 'veth99')
6654 print(output)
6655 self.assertIn('"InterfaceName":"veth99"', output)
6656 self.assertIn('"PortID":"veth-peer"', output)
6657 self.assertIn('"EnabledCapabilities":16', output)
6658
6659 # json format with interface name pattern
6660 output = networkctl('--json=short', 'lldp', 've*9')
6661 print(output)
6662 self.assertIn('"InterfaceName":"veth99"', output)
6663 self.assertIn('"PortID":"veth-peer"', output)
6664 self.assertIn('"EnabledCapabilities":16', output)
6665
6666 # LLDP neighbors in status
6667 output = networkctl_status('veth99')
6668 print(output)
6669 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6670
6671 # Compare the json output from sender and receiver
6672 sender_json = get_link_description('veth-peer')['LLDP']
6673 receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0]
6674 print(sender_json)
6675 print(receiver_json)
6676 self.assertEqual(sender_json, receiver_json)
6677
6678 class NetworkdRATests(unittest.TestCase, Utilities):
6679
6680 def setUp(self):
6681 setup_common()
6682
6683 def tearDown(self):
6684 tear_down_common()
6685
6686 def test_ipv6_prefix_delegation(self):
6687 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
6688 self.setup_nftset('addr6', 'ipv6_addr')
6689 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6690 self.setup_nftset('ifindex', 'iface_index')
6691 start_networkd()
6692 self.wait_online('veth99:routable', 'veth-peer:degraded')
6693
6694 # IPv6SendRA=yes implies IPv6Forwarding.
6695 self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
6696
6697 output = resolvectl('dns', 'veth99')
6698 print(output)
6699 self.assertRegex(output, 'fe80::')
6700 self.assertRegex(output, '2002:da8:1::1')
6701
6702 output = resolvectl('domain', 'veth99')
6703 print(output)
6704 self.assertIn('hogehoge.test', output)
6705
6706 output = networkctl_status('veth99')
6707 print(output)
6708 self.assertRegex(output, '2002:da8:1:0')
6709
6710 self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
6711 self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
6712
6713 self.check_netlabel('veth99', '2002:da8:1::/64')
6714 self.check_netlabel('veth99', '2002:da8:2::/64')
6715
6716 self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6717 self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6718 self.check_nftset('network6', '2002:da8:1::/64')
6719 self.check_nftset('network6', '2002:da8:2::/64')
6720 self.check_nftset('ifindex', 'veth99')
6721
6722 self.teardown_nftset('addr6', 'network6', 'ifindex')
6723
6724 def check_ipv6_token_static(self):
6725 self.wait_online('veth99:routable', 'veth-peer:degraded')
6726
6727 output = networkctl_status('veth99')
6728 print(output)
6729 self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
6730 self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
6731 self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
6732 self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
6733
6734 def test_ipv6_token_static(self):
6735 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6736 start_networkd()
6737
6738 self.check_ipv6_token_static()
6739
6740 for _ in range(20):
6741 check_output('ip link set veth99 down')
6742 check_output('ip link set veth99 up')
6743
6744 self.check_ipv6_token_static()
6745
6746 for _ in range(20):
6747 check_output('ip link set veth99 down')
6748 time.sleep(random.uniform(0, 0.1))
6749 check_output('ip link set veth99 up')
6750 time.sleep(random.uniform(0, 0.1))
6751
6752 self.check_ipv6_token_static()
6753
6754 def test_ndisc_redirect(self):
6755 if not os.path.exists(test_ndisc_send):
6756 self.skipTest(f"{test_ndisc_send} does not exist.")
6757
6758 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6759 start_networkd()
6760
6761 self.check_ipv6_token_static()
6762
6763 # Introduce three redirect routes.
6764 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
6765 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
6766 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d')
6767 self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6768 self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6769 self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6770
6771 # Change the target address of the redirects.
6772 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
6773 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
6774 self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6775 self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6776 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d nhid [0-9]* via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
6777 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d nhid [0-9]* via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
6778
6779 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
6780 # Then, verify that all redirect routes and the default route are dropped.
6781 output = check_output('ip -6 address show dev veth-peer scope link')
6782 veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
6783 print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
6784 check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
6785 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
6786 self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
6787
6788 # Check if sd-radv refuses RS from the same interface.
6789 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
6790 since = datetime.datetime.now()
6791 check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
6792 self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
6793
6794 def check_ndisc_mtu(self, mtu):
6795 for _ in range(20):
6796 output = read_ipv6_sysctl_attr('veth99', 'mtu')
6797 if output == f'{mtu}':
6798 break
6799 time.sleep(0.5)
6800 else:
6801 self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
6802
6803 def test_ndisc_mtu(self):
6804 if not os.path.exists(test_ndisc_send):
6805 self.skipTest(f"{test_ndisc_send} does not exist.")
6806
6807 copy_network_unit('25-veth.netdev',
6808 '25-veth-peer-no-address.network',
6809 '25-ipv6-prefix-veth-token-static.network')
6810 start_networkd()
6811 self.wait_online('veth-peer:degraded')
6812
6813 self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
6814
6815 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
6816 self.check_ndisc_mtu(1400)
6817
6818 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
6819 self.check_ndisc_mtu(1410)
6820
6821 check_output('ip link set dev veth99 mtu 1600')
6822 check_output('ip link set dev veth-peer mtu 1600')
6823 self.check_ndisc_mtu(1410)
6824
6825 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
6826 self.check_ndisc_mtu(1600)
6827
6828 check_output('ip link set dev veth99 mtu 1800')
6829 check_output('ip link set dev veth-peer mtu 1800')
6830 self.check_ndisc_mtu(1700)
6831
6832 def test_ipv6_token_prefixstable(self):
6833 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
6834 start_networkd()
6835 self.wait_online('veth99:routable', 'veth-peer:degraded')
6836
6837 output = check_output('ip -6 address show dev veth99')
6838 print(output)
6839 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6840 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6841
6842 with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
6843 f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
6844
6845 networkctl_reload()
6846 self.wait_online('veth99:routable')
6847
6848 output = check_output('ip -6 address show dev veth99')
6849 print(output)
6850 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6851 self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6852
6853 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
6854 check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
6855
6856 networkctl_reconfigure('veth99')
6857 self.wait_online('veth99:routable')
6858 self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6859 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
6860
6861 check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
6862 check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
6863
6864 networkctl_reconfigure('veth99')
6865 self.wait_online('veth99:routable')
6866 self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable
6867 self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6868 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
6869
6870 def test_ipv6_token_prefixstable_without_address(self):
6871 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
6872 start_networkd()
6873 self.wait_online('veth99:routable', 'veth-peer:degraded')
6874
6875 output = networkctl_status('veth99')
6876 print(output)
6877 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
6878 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
6879
6880 def test_router_hop_limit(self):
6881 copy_network_unit('25-veth-client.netdev',
6882 '25-veth-router.netdev',
6883 '26-bridge.netdev',
6884 '25-veth-bridge.network',
6885 '25-veth-client.network',
6886 '25-veth-router-hop-limit.network',
6887 '25-bridge99.network')
6888 start_networkd()
6889 self.wait_online('client:routable', 'client-p:enslaved',
6890 'router:degraded', 'router-p:enslaved',
6891 'bridge99:routable')
6892
6893 self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
6894
6895 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
6896 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
6897
6898 networkctl_reload()
6899
6900 for _ in range(20):
6901 output = read_ipv6_sysctl_attr('client', 'hop_limit')
6902 if output == '43':
6903 break
6904 time.sleep(0.5)
6905
6906 self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
6907
6908 def check_router_preference(self, suffix, metric_1, preference_1, metric_2, preference_2):
6909 self.wait_online('client:routable')
6910 self.wait_address('client', f'2002:da8:1:99:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6911 self.wait_address('client', f'2002:da8:1:98:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6912 self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1}', ipv='-6', timeout_sec=10)
6913 self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2}', ipv='-6', timeout_sec=10)
6914
6915 print('### ip -6 route show dev client default')
6916 output = check_output('ip -6 route show dev client default')
6917 print(output)
6918 self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1} expires [0-9]*sec pref {preference_1}')
6919 self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2} expires [0-9]*sec pref {preference_2}')
6920
6921 for i in [100, 200, 300, 512, 1024, 2048]:
6922 if i not in [metric_1, metric_2]:
6923 self.assertNotIn(f'metric {i} ', output)
6924
6925 for i in ['low', 'medium', 'high']:
6926 if i not in [preference_1, preference_2]:
6927 self.assertNotIn(f'pref {i}', output)
6928
6929 def test_router_preference(self):
6930 copy_network_unit('25-veth-client.netdev',
6931 '25-veth-router-high.netdev',
6932 '25-veth-router-low.netdev',
6933 '26-bridge.netdev',
6934 '25-veth-bridge.network',
6935 '25-veth-client.network',
6936 '25-veth-router-high.network',
6937 '25-veth-router-low.network',
6938 '25-bridge99.network')
6939 start_networkd()
6940 self.wait_online('client-p:enslaved',
6941 'router-high:degraded', 'router-high-p:enslaved',
6942 'router-low:degraded', 'router-low-p:enslaved',
6943 'bridge99:routable')
6944
6945 networkctl_reconfigure('client')
6946 self.wait_online('client:routable')
6947 self.check_router_preference('00', 512, 'high', 2048, 'low')
6948
6949 # change the map from preference to metric.
6950 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
6951 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
6952 networkctl_reload()
6953 self.check_router_preference('01', 100, 'high', 300, 'low')
6954
6955 # swap the preference (for issue #28439)
6956 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6957 f.write('\n[IPv6SendRA]\nRouterPreference=low\n')
6958 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6959 f.write('\n[IPv6SendRA]\nRouterPreference=high\n')
6960 networkctl_reload()
6961 self.check_router_preference('01', 300, 'low', 100, 'high')
6962
6963 # Use the same preference, and check if the two routes are not coalesced. See issue #33470.
6964 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6965 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
6966 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6967 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
6968 networkctl_reload()
6969 self.check_router_preference('01', 200, 'medium', 200, 'medium')
6970
6971 # Use route options to configure default routes.
6972 # The preference specified in the RA header should be ignored. See issue #33468.
6973 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6974 f.write('\n[IPv6SendRA]\nRouterPreference=high\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6975 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6976 f.write('\n[IPv6SendRA]\nRouterPreference=low\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6977 networkctl_reload()
6978 self.check_router_preference('01', 200, 'medium', 200, 'medium')
6979
6980 # Set zero lifetime to the route options.
6981 # The preference specified in the RA header should be used.
6982 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6983 f.write('LifetimeSec=0\n')
6984 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6985 f.write('LifetimeSec=0\n')
6986 networkctl_reload()
6987 self.check_router_preference('01', 100, 'high', 300, 'low')
6988
6989 # Use route options with preference to configure default routes.
6990 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6991 f.write('LifetimeSec=1200\nPreference=low\n')
6992 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6993 f.write('LifetimeSec=1200\nPreference=high\n')
6994 networkctl_reload()
6995 self.check_router_preference('01', 300, 'low', 100, 'high')
6996
6997 # Set zero lifetime again to the route options.
6998 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6999 f.write('LifetimeSec=0\n')
7000 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
7001 f.write('LifetimeSec=0\n')
7002 networkctl_reload()
7003 self.check_router_preference('01', 100, 'high', 300, 'low')
7004
7005 def _test_ndisc_vs_static_route(self, manage_foreign_nexthops):
7006 if not manage_foreign_nexthops:
7007 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
7008 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-static-route.network')
7009 start_networkd()
7010 self.wait_online('veth99:routable', 'veth-peer:degraded')
7011
7012 # If a conflicting static route is already configured, do not override the static route.
7013 print('### ip -6 route show dev veth99 default')
7014 output = check_output('ip -6 route show dev veth99 default')
7015 print(output)
7016 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
7017 if manage_foreign_nexthops:
7018 self.assertRegex(output, r'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium')
7019 else:
7020 self.assertNotIn('proto ra', output)
7021
7022 print('### ip -6 nexthop show dev veth99')
7023 output = check_output('ip -6 nexthop show dev veth99')
7024 print(output)
7025 if manage_foreign_nexthops:
7026 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra')
7027 else:
7028 self.assertEqual(output, '')
7029
7030 # Also check if the static route is protected from RA with zero lifetime
7031 with open(os.path.join(network_unit_dir, '25-ipv6-prefix.network'), mode='a', encoding='utf-8') as f:
7032 f.write('\n[Network]\nIPv6SendRA=no\n')
7033 networkctl_reload() # This makes veth-peer being reconfigured, and send RA with zero lifetime
7034 self.wait_route_dropped('veth99', r'default (nhid [0-9]* |)via fe80::1034:56ff:fe78:9abd proto ra metric 256', ipv='-6', timeout_sec=10)
7035
7036 print('### ip -6 route show dev veth99 default')
7037 output = check_output('ip -6 route show dev veth99 default')
7038 print(output)
7039 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
7040 self.assertNotIn('proto ra', output)
7041
7042 # Check if nexthop is removed.
7043 print('### ip -6 nexthop show dev veth99')
7044 output = check_output('ip -6 nexthop show dev veth99')
7045 print(output)
7046 self.assertEqual(output, '')
7047
7048 def test_ndisc_vs_static_route(self):
7049 first = True
7050 for manage_foreign_nexthops in [True, False]:
7051 if first:
7052 first = False
7053 else:
7054 self.tearDown()
7055
7056 print(f'### test_ndisc_vs_static_route(manage_foreign_nexthops={manage_foreign_nexthops})')
7057 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
7058 self._test_ndisc_vs_static_route(manage_foreign_nexthops)
7059
7060 # radvd supports captive portal since v2.20.
7061 # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc
7062 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7063 def test_captive_portal(self):
7064 copy_network_unit('25-veth-client.netdev',
7065 '25-veth-router-captive.netdev',
7066 '26-bridge.netdev',
7067 '25-veth-client-captive.network',
7068 '25-veth-router-captive.network',
7069 '25-veth-bridge-captive.network',
7070 '25-bridge99.network')
7071 start_networkd()
7072 self.wait_online('bridge99:routable', 'client-p:enslaved',
7073 'router-captive:degraded', 'router-captivep:enslaved')
7074
7075 start_radvd(config_file='captive-portal.conf')
7076 networkctl_reconfigure('client')
7077 self.wait_online('client:routable')
7078
7079 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
7080 output = networkctl_status('client')
7081 print(output)
7082 self.assertIn('Captive Portal: http://systemd.io', output)
7083
7084 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7085 def test_invalid_captive_portal(self):
7086 def radvd_write_config(captive_portal_uri):
7087 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
7088 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
7089
7090 captive_portal_uris = [
7091 "42ěščěškd ěšč ě s",
7092 " ",
7093 "🤔",
7094 ]
7095
7096 copy_network_unit('25-veth-client.netdev',
7097 '25-veth-router-captive.netdev',
7098 '26-bridge.netdev',
7099 '25-veth-client-captive.network',
7100 '25-veth-router-captive.network',
7101 '25-veth-bridge-captive.network',
7102 '25-bridge99.network')
7103 start_networkd()
7104 self.wait_online('bridge99:routable', 'client-p:enslaved',
7105 'router-captive:degraded', 'router-captivep:enslaved')
7106
7107 for uri in captive_portal_uris:
7108 print(f"Captive portal: {uri}")
7109 radvd_write_config(uri)
7110 stop_radvd()
7111 start_radvd(config_file='bogus-captive-portal.conf')
7112 networkctl_reconfigure('client')
7113 self.wait_online('client:routable')
7114
7115 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
7116 output = networkctl_status('client')
7117 print(output)
7118 self.assertNotIn('Captive Portal:', output)
7119
7120 class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
7121
7122 def setUp(self):
7123 setup_common()
7124
7125 def tearDown(self):
7126 tear_down_common()
7127
7128 def check_dhcp_server(self, persist_leases='yes'):
7129 output = networkctl_status('veth99')
7130 print(output)
7131 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7132 self.assertIn('Gateway: 192.168.5.3', output)
7133 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
7134 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
7135
7136 output = networkctl_status('veth-peer')
7137 print(output)
7138 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
7139
7140 if persist_leases == 'yes':
7141 path = '/var/lib/systemd/network/dhcp-server-lease/veth-peer'
7142 elif persist_leases == 'runtime':
7143 path = '/run/systemd/netif/dhcp-server-lease/veth-peer'
7144 else:
7145 path = None
7146
7147 if path:
7148 with open(path, encoding='utf-8') as f:
7149 check_json(f.read())
7150
7151 def test_dhcp_server(self):
7152 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7153 start_networkd()
7154 self.wait_online('veth99:routable', 'veth-peer:routable')
7155
7156 self.check_dhcp_server()
7157
7158 networkctl_reconfigure('veth-peer')
7159 self.wait_online('veth-peer:routable')
7160
7161 for _ in range(10):
7162 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
7163 if 'Offered DHCP leases: 192.168.5.' in output:
7164 break
7165 time.sleep(.2)
7166 else:
7167 self.fail()
7168
7169 def test_dhcp_server_persist_leases_no(self):
7170 copy_networkd_conf_dropin('persist-leases-no.conf')
7171 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7172 start_networkd()
7173 self.wait_online('veth99:routable', 'veth-peer:routable')
7174
7175 self.check_dhcp_server(persist_leases='no')
7176
7177 remove_networkd_conf_dropin('persist-leases-no.conf')
7178 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7179 f.write('[DHCPServer]\nPersistLeases=no')
7180 restart_networkd()
7181 self.wait_online('veth99:routable', 'veth-peer:routable')
7182
7183 self.check_dhcp_server(persist_leases='no')
7184
7185 def test_dhcp_server_persist_leases_runtime(self):
7186 copy_networkd_conf_dropin('persist-leases-runtime.conf')
7187 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7188 start_networkd()
7189 self.wait_online('veth99:routable', 'veth-peer:routable')
7190
7191 self.check_dhcp_server(persist_leases='runtime')
7192
7193 remove_networkd_conf_dropin('persist-leases-runtime.conf')
7194 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7195 f.write('[DHCPServer]\nPersistLeases=runtime')
7196 restart_networkd()
7197 self.wait_online('veth99:routable', 'veth-peer:routable')
7198
7199 self.check_dhcp_server(persist_leases='runtime')
7200
7201 def test_dhcp_server_null_server_address(self):
7202 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
7203 start_networkd()
7204 self.wait_online('veth99:routable', 'veth-peer:routable')
7205
7206 output = check_output('ip --json address show dev veth-peer')
7207 server_address = json.loads(output)[0]['addr_info'][0]['local']
7208 print(server_address)
7209
7210 output = check_output('ip --json address show dev veth99')
7211 client_address = json.loads(output)[0]['addr_info'][0]['local']
7212 print(client_address)
7213
7214 output = networkctl_status('veth99')
7215 print(output)
7216 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
7217 self.assertIn(f'Gateway: {server_address}', output)
7218 self.assertIn(f'DNS: {server_address}', output)
7219 self.assertIn(f'NTP: {server_address}', output)
7220
7221 output = networkctl_status('veth-peer')
7222 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7223
7224 # Check if the same addresses are used even if the service is restarted.
7225 restart_networkd()
7226 self.wait_online('veth99:routable', 'veth-peer:routable')
7227
7228 output = check_output('ip -4 address show dev veth-peer')
7229 print(output)
7230 self.assertIn(f'{server_address}', output)
7231
7232 output = check_output('ip -4 address show dev veth99')
7233 print(output)
7234 self.assertIn(f'{client_address}', output)
7235
7236 output = networkctl_status('veth99')
7237 print(output)
7238 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
7239 self.assertIn(f'Gateway: {server_address}', output)
7240 self.assertIn(f'DNS: {server_address}', output)
7241 self.assertIn(f'NTP: {server_address}', output)
7242
7243 output = networkctl_status('veth-peer')
7244 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7245
7246 def test_dhcp_server_with_uplink(self):
7247 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
7248 '12-dummy.netdev', '25-dhcp-server-uplink.network')
7249 start_networkd()
7250 self.wait_online('veth99:routable', 'veth-peer:routable')
7251
7252 output = networkctl_status('veth99')
7253 print(output)
7254 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7255 self.assertIn('Gateway: 192.168.5.3', output)
7256 self.assertIn('DNS: 192.168.5.1', output)
7257 self.assertIn('NTP: 192.168.5.1', output)
7258
7259 def test_emit_router_timezone(self):
7260 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
7261 start_networkd()
7262 self.wait_online('veth99:routable', 'veth-peer:routable')
7263
7264 output = networkctl_status('veth99')
7265 print(output)
7266 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7267 self.assertIn('Gateway: 192.168.5.1', output)
7268 self.assertIn('Time Zone: Europe/Berlin', output)
7269
7270 def test_dhcp_server_static_lease_mac_by_network(self):
7271 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
7272 copy_networkd_conf_dropin('10-dhcp-client-id-duid.conf')
7273 start_networkd()
7274 self.wait_online('veth99:routable', 'veth-peer:routable')
7275
7276 output = networkctl_status('veth99')
7277 print(output)
7278 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7279 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
7280
7281 def test_dhcp_server_static_lease_mac_by_global(self):
7282 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7283 copy_networkd_conf_dropin('10-dhcp-client-id-mac.conf')
7284 start_networkd()
7285 self.wait_online('veth99:routable', 'veth-peer:routable')
7286
7287 output = networkctl_status('veth99')
7288 print(output)
7289 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7290 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
7291
7292 def test_dhcp_server_static_lease_duid(self):
7293 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7294 start_networkd()
7295 self.wait_online('veth99:routable', 'veth-peer:routable')
7296
7297 output = networkctl_status('veth99')
7298 print(output)
7299 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7300 self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
7301
7302 class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
7303
7304 def setUp(self):
7305 setup_common()
7306
7307 def tearDown(self):
7308 tear_down_common()
7309
7310 def test_relay_agent(self):
7311 copy_network_unit('25-agent-veth-client.netdev',
7312 '25-agent-veth-server.netdev',
7313 '25-agent-client.network',
7314 '25-agent-server.network',
7315 '25-agent-client-peer.network',
7316 '25-agent-server-peer.network')
7317 start_networkd()
7318
7319 self.wait_online('client:routable')
7320
7321 output = networkctl_status('client')
7322 print(output)
7323 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)')
7324
7325 def test_relay_agent_on_bridge(self):
7326 copy_network_unit('25-agent-bridge.netdev',
7327 '25-agent-veth-client.netdev',
7328 '25-agent-bridge.network',
7329 '25-agent-bridge-port.network',
7330 '25-agent-client.network')
7331 start_networkd()
7332 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
7333
7334 # For issue #30763.
7335 self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
7336
7337 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
7338
7339 def setUp(self):
7340 setup_common()
7341
7342 def tearDown(self):
7343 tear_down_common()
7344
7345 def test_dhcp_client_ipv6_only(self):
7346 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
7347
7348 start_networkd()
7349 self.wait_online('veth-peer:carrier')
7350
7351 # information request mode
7352 # The name ipv6-only option may not be supported by older dnsmasq
7353 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
7354 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7355 '--dhcp-option=option6:dns-server,[2600::ee]',
7356 '--dhcp-option=option6:ntp-server,[2600::ff]',
7357 ra_mode='ra-stateless')
7358 self.wait_online('veth99:routable', 'veth-peer:routable')
7359
7360 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
7361 # Let's wait for the expected DNS server being listed in the state file.
7362 for _ in range(100):
7363 output = read_link_state_file('veth99')
7364 if 'DNS=2600::ee' in output:
7365 break
7366 time.sleep(.2)
7367
7368 # Check link state file
7369 print('## link state file')
7370 output = read_link_state_file('veth99')
7371 print(output)
7372 self.assertIn('DNS=2600::ee', output)
7373 self.assertIn('NTP=2600::ff', output)
7374
7375 # Check manager state file
7376 print('## manager state file')
7377 output = read_manager_state_file()
7378 print(output)
7379 self.assertRegex(output, 'DNS=.*2600::ee')
7380 self.assertRegex(output, 'NTP=.*2600::ff')
7381
7382 print('## dnsmasq log')
7383 output = read_dnsmasq_log_file()
7384 print(output)
7385 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7386 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
7387 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7388 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7389 self.assertNotIn('DHCPREPLY(veth-peer)', output)
7390
7391 # Check json format
7392 check_json(networkctl_json('veth99'))
7393
7394 # solicit mode
7395 stop_dnsmasq()
7396 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7397 '--dhcp-option=option6:dns-server,[2600::ee]',
7398 '--dhcp-option=option6:ntp-server,[2600::ff]')
7399 networkctl_reconfigure('veth99')
7400 self.wait_online('veth99:routable', 'veth-peer:routable')
7401
7402 # checking address
7403 output = check_output('ip address show dev veth99 scope global')
7404 print(output)
7405 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7406 self.assertNotIn('192.168.5', output)
7407
7408 # checking semi-static route
7409 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7410 print(output)
7411 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7412
7413 # Confirm that ipv6 token is not set in the kernel
7414 output = check_output('ip token show dev veth99')
7415 print(output)
7416 self.assertRegex(output, 'token :: dev veth99')
7417
7418 # Make manager and link state file updated
7419 resolvectl('revert', 'veth99')
7420
7421 # Check link state file
7422 print('## link state file')
7423 output = read_link_state_file('veth99')
7424 print(output)
7425 self.assertIn('DNS=2600::ee', output)
7426 self.assertIn('NTP=2600::ff', output)
7427
7428 # Check manager state file
7429 print('## manager state file')
7430 output = read_manager_state_file()
7431 print(output)
7432 self.assertRegex(output, 'DNS=.*2600::ee')
7433 self.assertRegex(output, 'NTP=.*2600::ff')
7434
7435 print('## dnsmasq log')
7436 output = read_dnsmasq_log_file()
7437 print(output)
7438 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7439 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7440 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7441 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7442 self.assertIn('DHCPREPLY(veth-peer)', output)
7443 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7444
7445 # Check json format
7446 check_json(networkctl_json('veth99'))
7447
7448 # Testing without rapid commit support
7449 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
7450 f.write('\n[DHCPv6]\nRapidCommit=no\n')
7451
7452 stop_dnsmasq()
7453 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7454 '--dhcp-option=option6:dns-server,[2600::ee]',
7455 '--dhcp-option=option6:ntp-server,[2600::ff]')
7456
7457 networkctl_reload()
7458 self.wait_online('veth99:routable', 'veth-peer:routable')
7459
7460 # checking address
7461 output = check_output('ip address show dev veth99 scope global')
7462 print(output)
7463 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7464 self.assertNotIn('192.168.5', output)
7465
7466 # checking semi-static route
7467 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7468 print(output)
7469 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7470
7471 # Make manager and link state file updated
7472 resolvectl('revert', 'veth99')
7473
7474 # Check link state file
7475 print('## link state file')
7476 output = read_link_state_file('veth99')
7477 print(output)
7478 self.assertIn('DNS=2600::ee', output)
7479 self.assertIn('NTP=2600::ff', output)
7480
7481 # Check manager state file
7482 print('## manager state file')
7483 output = read_manager_state_file()
7484 print(output)
7485 self.assertRegex(output, 'DNS=.*2600::ee')
7486 self.assertRegex(output, 'NTP=.*2600::ff')
7487
7488 print('## dnsmasq log')
7489 output = read_dnsmasq_log_file()
7490 print(output)
7491 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7492 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7493 self.assertIn('DHCPADVERTISE(veth-peer)', output)
7494 self.assertIn('DHCPREQUEST(veth-peer)', output)
7495 self.assertIn('DHCPREPLY(veth-peer)', output)
7496 self.assertNotIn('rapid-commit', output)
7497
7498 # Check json format
7499 check_json(networkctl_json('veth99'))
7500
7501 def test_dhcp_client_ipv6_dbus_status(self):
7502 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
7503 start_networkd()
7504 self.wait_online('veth-peer:carrier')
7505
7506 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
7507 # bit set) has yet been received and the configuration does not include WithoutRA=true
7508 state = get_dhcp6_client_state('veth99')
7509 print(f"DHCPv6 client state = {state}")
7510 self.assertEqual(state, 'stopped')
7511
7512 state = get_dhcp4_client_state('veth99')
7513 print(f"DHCPv4 client state = {state}")
7514 self.assertEqual(state, 'selecting')
7515
7516 start_dnsmasq('--dhcp-option=108,00:00:02:00')
7517 self.wait_online('veth99:routable', 'veth-peer:routable')
7518
7519 state = get_dhcp6_client_state('veth99')
7520 print(f"DHCPv6 client state = {state}")
7521 self.assertEqual(state, 'bound')
7522
7523 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
7524 for _ in range(100):
7525 state = get_dhcp4_client_state('veth99')
7526 if state == 'stopped':
7527 break
7528 time.sleep(.2)
7529
7530 print(f"DHCPv4 client state = {state}")
7531 self.assertEqual(state, 'stopped')
7532
7533 # restart dnsmasq to clear log
7534 stop_dnsmasq()
7535 start_dnsmasq('--dhcp-option=108,00:00:02:00')
7536
7537 # Test renew command
7538 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
7539 networkctl('renew', 'veth99')
7540
7541 for _ in range(100):
7542 state = get_dhcp4_client_state('veth99')
7543 if state == 'stopped':
7544 break
7545 time.sleep(.2)
7546
7547 print(f"DHCPv4 client state = {state}")
7548 self.assertEqual(state, 'stopped')
7549
7550 print('## dnsmasq log')
7551 output = read_dnsmasq_log_file()
7552 print(output)
7553 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
7554 self.assertIn('DHCPOFFER(veth-peer)', output)
7555 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7556 self.assertNotIn('DHCPACK(veth-peer)', output)
7557
7558 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
7559 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
7560
7561 start_networkd()
7562 self.wait_online('veth-peer:carrier')
7563 start_dnsmasq()
7564 self.wait_online('veth99:routable', 'veth-peer:routable')
7565
7566 # checking address
7567 output = check_output('ip address show dev veth99 scope global')
7568 print(output)
7569 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7570 self.assertNotIn('192.168.5', output)
7571
7572 print('## dnsmasq log')
7573 output = read_dnsmasq_log_file()
7574 print(output)
7575 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
7576 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7577 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7578 self.assertIn('DHCPREPLY(veth-peer)', output)
7579 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7580
7581 @expectedFailureIfKernelReturnsInvalidFlags()
7582 def test_dhcp_client_ipv4_only(self):
7583 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network',
7584 '25-sit-dhcp4.netdev', '25-sit-dhcp4.network')
7585
7586 self.setup_nftset('addr4', 'ipv4_addr')
7587 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
7588 self.setup_nftset('ifindex', 'iface_index')
7589
7590 start_networkd()
7591 self.wait_online('veth-peer:carrier')
7592 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
7593 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
7594 '--dhcp-option=option:domain-search,example.com',
7595 '--dhcp-alternate-port=67,5555',
7596 ipv4_range='192.168.5.110,192.168.5.119')
7597 self.wait_online('veth99:routable', 'veth-peer:routable', 'sit-dhcp4:carrier')
7598 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
7599
7600 print('## ip address show dev veth99 scope global')
7601 output = check_output('ip address show dev veth99 scope global')
7602 print(output)
7603 self.assertIn('mtu 1492', output)
7604 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
7605 self.assertRegex(output, r'inet 192.168.5.11[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label')
7606 self.assertNotIn('2600::', output)
7607
7608 output = check_output('ip -4 --json address show dev veth99')
7609 for i in json.loads(output)[0]['addr_info']:
7610 if i['label'] == 'test-label':
7611 address1 = i['local']
7612 break
7613 else:
7614 self.assertFalse(True)
7615
7616 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
7617
7618 print('## ip route show table main dev veth99')
7619 output = check_output('ip route show table main dev veth99')
7620 print(output)
7621 # no DHCP routes assigned to the main table
7622 self.assertNotIn('proto dhcp', output)
7623 # static routes
7624 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7625 self.assertIn('192.168.5.0/24 proto static scope link', output)
7626 self.assertIn('192.168.6.0/24 proto static scope link', output)
7627 self.assertIn('192.168.7.0/24 proto static scope link', output)
7628
7629 print('## ip route show table 211 dev veth99')
7630 output = check_output('ip route show table 211 dev veth99')
7631 print(output)
7632 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
7633 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7634 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7635 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
7636 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
7637 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1}')
7638
7639 print('## ip route show table 212 dev veth99')
7640 output = check_output('ip route show table 212 dev veth99')
7641 print(output)
7642 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7643 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7644 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1}')
7645
7646 print('## link state file')
7647 output = read_link_state_file('veth99')
7648 print(output)
7649 # checking DNS server, SIP server, and Domains
7650 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
7651 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
7652 self.assertIn('DOMAINS=example.com', output)
7653
7654 print('## json')
7655 j = json.loads(networkctl_json('veth99'))
7656
7657 self.assertEqual(len(j['DNS']), 2)
7658 for i in j['DNS']:
7659 print(i)
7660 self.assertEqual(i['Family'], 2)
7661 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7662 self.assertRegex(a, '^192.168.5.[67]$')
7663 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7664 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7665 self.assertEqual('192.168.5.1', a)
7666
7667 self.assertEqual(len(j['SIP']), 2)
7668 for i in j['SIP']:
7669 print(i)
7670 self.assertEqual(i['Family'], 2)
7671 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7672 self.assertRegex(a, '^192.168.5.2[12]$')
7673 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7674 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7675 self.assertEqual('192.168.5.1', a)
7676
7677 print('## tunnel')
7678 output = check_output('ip -d link show sit-dhcp4')
7679 print(output)
7680 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address1} dev veth99')
7681
7682 print('## dnsmasq log')
7683 output = read_dnsmasq_log_file()
7684 print(output)
7685 self.assertIn('vendor class: FooBarVendorTest', output)
7686 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
7687 self.assertIn('client provides name: test-hostname', output)
7688 self.assertIn('26:mtu', output)
7689
7690 # change address range, DNS servers, and Domains
7691 stop_dnsmasq()
7692 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
7693 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
7694 '--dhcp-option=option:domain-search,foo.example.com',
7695 '--dhcp-alternate-port=67,5555',
7696 ipv4_range='192.168.5.120,192.168.5.129',)
7697
7698 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
7699 print('Wait for the DHCP lease to be expired')
7700 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
7701 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
7702
7703 self.wait_online('veth99:routable', 'veth-peer:routable')
7704
7705 print('## ip address show dev veth99 scope global')
7706 output = check_output('ip address show dev veth99 scope global')
7707 print(output)
7708 self.assertIn('mtu 1492', output)
7709 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
7710 self.assertNotIn(f'{address1}', output)
7711 self.assertRegex(output, r'inet 192.168.5.12[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label')
7712 self.assertNotIn('2600::', output)
7713
7714 output = check_output('ip -4 --json address show dev veth99')
7715 for i in json.loads(output)[0]['addr_info']:
7716 if i['label'] == 'test-label':
7717 address2 = i['local']
7718 break
7719 else:
7720 self.assertFalse(True)
7721
7722 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
7723
7724 print('## ip route show table main dev veth99')
7725 output = check_output('ip route show table main dev veth99')
7726 print(output)
7727 # no DHCP routes assigned to the main table
7728 self.assertNotIn('proto dhcp', output)
7729 # static routes
7730 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7731 self.assertIn('192.168.5.0/24 proto static scope link', output)
7732 self.assertIn('192.168.6.0/24 proto static scope link', output)
7733 self.assertIn('192.168.7.0/24 proto static scope link', output)
7734
7735 print('## ip route show table 211 dev veth99')
7736 output = check_output('ip route show table 211 dev veth99')
7737 print(output)
7738 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
7739 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7740 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
7741 self.assertNotIn('192.168.5.6', output)
7742 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
7743 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
7744 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2}')
7745
7746 print('## ip route show table 212 dev veth99')
7747 output = check_output('ip route show table 212 dev veth99')
7748 print(output)
7749 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7750 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
7751 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2}')
7752
7753 print('## link state file')
7754 output = read_link_state_file('veth99')
7755 print(output)
7756 # checking DNS server, SIP server, and Domains
7757 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
7758 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
7759 self.assertIn('DOMAINS=foo.example.com', output)
7760
7761 print('## json')
7762 j = json.loads(networkctl_json('veth99'))
7763
7764 self.assertEqual(len(j['DNS']), 3)
7765 for i in j['DNS']:
7766 print(i)
7767 self.assertEqual(i['Family'], 2)
7768 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7769 self.assertRegex(a, '^192.168.5.[178]$')
7770 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7771 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7772 self.assertEqual('192.168.5.1', a)
7773
7774 self.assertEqual(len(j['SIP']), 2)
7775 for i in j['SIP']:
7776 print(i)
7777 self.assertEqual(i['Family'], 2)
7778 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7779 self.assertRegex(a, '^192.168.5.2[34]$')
7780 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7781 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7782 self.assertEqual('192.168.5.1', a)
7783
7784 print('## tunnel')
7785 output = check_output('ip -d link show sit-dhcp4')
7786 print(output)
7787 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address2} dev veth99')
7788
7789 print('## dnsmasq log')
7790 output = read_dnsmasq_log_file()
7791 print(output)
7792 self.assertIn('vendor class: FooBarVendorTest', output)
7793 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
7794 self.assertIn('client provides name: test-hostname', output)
7795 self.assertIn('26:mtu', output)
7796
7797 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
7798
7799 self.check_nftset('addr4', r'192\.168\.5\.1')
7800 self.check_nftset('network4', r'192\.168\.5\.0/24')
7801 self.check_nftset('ifindex', 'veth99')
7802
7803 # Check if DHCPv4 address and routes are removed on stop. For issue #34837.
7804 stop_networkd(show_logs=False)
7805 self.wait_address_dropped('veth99', f'inet {address2}/24', ipv='-4', timeout_sec=120)
7806
7807 print('## ip address show dev veth99 scope global')
7808 output = check_output('ip address show dev veth99 scope global')
7809 print(output)
7810 self.assertNotIn(f'{address2}', output)
7811
7812 print('## ip route show table main dev veth99')
7813 output = check_output('ip route show table main dev veth99')
7814 print(output)
7815 self.assertNotIn(f'{address2}', output)
7816
7817 print('## ip route show table 211 dev veth99')
7818 output = check_output('ip route show table 211 dev veth99')
7819 print(output)
7820 self.assertNotIn(f'{address2}', output)
7821
7822 print('## ip route show table 212 dev veth99')
7823 output = check_output('ip route show table 212 dev veth99')
7824 print(output)
7825 self.assertNotIn(f'{address2}', output)
7826
7827 self.teardown_nftset('addr4', 'network4', 'ifindex')
7828
7829 def test_dhcp_client_ipv4_dbus_status(self):
7830 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
7831 start_networkd()
7832 self.wait_online('veth-peer:carrier')
7833
7834 state = get_dhcp4_client_state('veth99')
7835 print(f"State = {state}")
7836 self.assertEqual(state, 'rebooting')
7837
7838 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
7839 '--dhcp-option=option:domain-search,example.com',
7840 '--dhcp-alternate-port=67,5555',
7841 ipv4_range='192.168.5.110,192.168.5.119')
7842 self.wait_online('veth99:routable', 'veth-peer:routable')
7843 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
7844
7845 state = get_dhcp4_client_state('veth99')
7846 print(f"State = {state}")
7847 self.assertEqual(state, 'bound')
7848
7849 def test_dhcp_client_allow_list(self):
7850 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
7851
7852 start_networkd()
7853 self.wait_online('veth-peer:carrier')
7854 since = datetime.datetime.now()
7855 start_dnsmasq()
7856
7857 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
7858
7859 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
7860 since = datetime.datetime.now()
7861 networkctl_reload()
7862
7863 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
7864
7865 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
7866 since = datetime.datetime.now()
7867 networkctl_reload()
7868
7869 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
7870
7871 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
7872 def test_dhcp_client_rapid_commit(self):
7873 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
7874 start_networkd()
7875 self.wait_online('veth-peer:carrier')
7876
7877 start_dnsmasq('--dhcp-rapid-commit')
7878 self.wait_online('veth99:routable', 'veth-peer:routable')
7879 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7880
7881 state = get_dhcp4_client_state('veth99')
7882 print(f"DHCPv4 client state = {state}")
7883 self.assertEqual(state, 'bound')
7884
7885 output = read_dnsmasq_log_file()
7886 self.assertIn('DHCPDISCOVER(veth-peer)', output)
7887 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7888 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7889 self.assertIn('DHCPACK(veth-peer)', output)
7890
7891 def check_bootp_client(self, check_log):
7892 self.wait_online('veth99:routable', 'veth-peer:routable')
7893 output = check_output('ip -4 address show dev veth99')
7894 print(output)
7895 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24')
7896
7897 state = get_dhcp4_client_state('veth99')
7898 print(f"DHCPv4 client state = {state}")
7899 self.assertEqual(state, 'bound')
7900
7901 if check_log:
7902 output = read_dnsmasq_log_file()
7903 print(output)
7904 self.assertIn('BOOTP(veth-peer)', output)
7905 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7906 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7907 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7908 self.assertNotIn('DHCPACK(veth-peer)', output)
7909
7910 def test_bootp_client(self):
7911 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-bootp-client.network')
7912 start_networkd()
7913 self.wait_online('veth-peer:carrier')
7914 start_dnsmasq('--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule')
7915 self.check_bootp_client(check_log=True)
7916
7917 touch_network_unit('25-bootp-client.network')
7918 networkctl_reload()
7919 self.check_bootp_client(check_log=True)
7920
7921 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7922 f.write('[DHCPv4]\nBOOTP=no\n')
7923
7924 networkctl_reload()
7925 self.check_bootp_client(check_log=False)
7926
7927 output = read_dnsmasq_log_file()
7928 print(output)
7929 # Note, on reload, the DHCP client will be started from INIT-REBOOT state,
7930 # hence DISCOVER and OFFER message will not be sent/received.
7931 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7932 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7933 self.assertIn('DHCPREQUEST(veth-peer)', output)
7934 self.assertIn('DHCPACK(veth-peer)', output)
7935
7936 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7937 f.write('[DHCPv4]\nBOOTP=yes\n')
7938
7939 since = datetime.datetime.now()
7940
7941 networkctl_reload()
7942 self.check_bootp_client(check_log=False)
7943
7944 # Check if the client send RELEASE message of the previous lease
7945 self.check_networkd_log('veth99: DHCPv4 client: RELEASE', since=since)
7946
7947 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
7948 copy_network_unit('25-veth.netdev',
7949 '25-dhcp-server-ipv6-only-mode.network',
7950 '25-dhcp-client-ipv6-only-mode.network')
7951 start_networkd()
7952 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
7953 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7954
7955 state = get_dhcp4_client_state('veth99')
7956 print(f"State = {state}")
7957 self.assertEqual(state, 'bound')
7958
7959 def test_dhcp_client_ipv4_use_routes_gateway(self):
7960 first = True
7961 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
7962 if first:
7963 first = False
7964 else:
7965 self.tearDown()
7966
7967 print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
7968 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
7969 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
7970
7971 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
7972 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
7973 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
7974 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
7975 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
7976 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
7977 copy_network_unit(*testunits, copy_dropins=False)
7978
7979 start_networkd()
7980 self.wait_online('veth-peer:carrier')
7981 additional_options = [
7982 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
7983 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
7984 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
7985 ]
7986 if classless:
7987 additional_options += [
7988 '--dhcp-option=option:classless-static-route,0.0.0.0/0,192.168.5.4,8.0.0.0/8,192.168.5.5,192.168.5.64/26,192.168.5.5'
7989 ]
7990 start_dnsmasq(*additional_options)
7991 self.wait_online('veth99:routable', 'veth-peer:routable')
7992
7993 output = check_output('ip -4 route show dev veth99')
7994 print(output)
7995
7996 # Check UseRoutes=
7997 if use_routes:
7998 if classless:
7999 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8000 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8001 self.assertRegex(output, r'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8002 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8003 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8004 else:
8005 self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
8006 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8007 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8008 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8009 else:
8010 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8011 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8012 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8013 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8014 self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
8015 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8016 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8017 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8018
8019 # Check UseGateway=
8020 if use_gateway and (not classless or not use_routes):
8021 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8022 else:
8023 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8024
8025 # Check route to gateway
8026 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
8027 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8028 else:
8029 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8030
8031 # Check RoutesToDNS= and RoutesToNTP=
8032 if dns_and_ntp_routes:
8033 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8034 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8035 if use_routes:
8036 if classless:
8037 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8038 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8039 else:
8040 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8041 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8042 else:
8043 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8044 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8045 else:
8046 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8047 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8048 self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
8049 self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
8050
8051 check_json(networkctl_json())
8052
8053 def test_dhcp_client_settings_anonymize(self):
8054 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
8055 start_networkd()
8056 self.wait_online('veth-peer:carrier')
8057 start_dnsmasq()
8058 self.wait_online('veth99:routable', 'veth-peer:routable')
8059
8060 print('## dnsmasq log')
8061 output = read_dnsmasq_log_file()
8062 print(output)
8063 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
8064 self.assertNotIn('test-hostname', output)
8065 self.assertNotIn('26:mtu', output)
8066
8067 def test_dhcp_keep_configuration_dynamic(self):
8068 copy_network_unit('25-veth.netdev',
8069 '25-dhcp-server-veth-peer.network',
8070 '25-dhcp-client-keep-configuration-dynamic.network')
8071 start_networkd()
8072 self.wait_online('veth-peer:carrier')
8073 start_dnsmasq()
8074 self.wait_online('veth99:routable', 'veth-peer:routable')
8075
8076 output = check_output('ip address show dev veth99 scope global')
8077 print(output)
8078 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8079 'valid_lft forever preferred_lft forever')
8080
8081 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
8082 stop_dnsmasq()
8083
8084 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
8085 print('Wait for the DHCP lease to be expired')
8086 time.sleep(120)
8087
8088 # The lease address should be kept after the lease expired
8089 output = check_output('ip address show dev veth99 scope global')
8090 print(output)
8091 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8092 'valid_lft forever preferred_lft forever')
8093
8094 stop_networkd()
8095
8096 # The lease address should be kept after networkd stopped
8097 output = check_output('ip address show dev veth99 scope global')
8098 print(output)
8099 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8100 'valid_lft forever preferred_lft forever')
8101
8102 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f:
8103 f.write('[Network]\nDHCP=no\n')
8104
8105 start_networkd()
8106 self.wait_online('veth99:routable', 'veth-peer:routable')
8107
8108 # Still the lease address should be kept after networkd restarted
8109 output = check_output('ip address show dev veth99 scope global')
8110 print(output)
8111 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8112 'valid_lft forever preferred_lft forever')
8113
8114 def test_dhcp_keep_configuration_dynamic_on_stop(self):
8115 copy_network_unit('25-veth.netdev',
8116 '25-dhcp-server-veth-peer.network',
8117 '25-dhcp-client-keep-configuration-dynamic-on-stop.network')
8118 start_networkd()
8119 self.wait_online('veth-peer:carrier')
8120 start_dnsmasq()
8121 self.wait_online('veth99:routable', 'veth-peer:routable')
8122
8123 output = check_output('ip address show dev veth99 scope global')
8124 print(output)
8125 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8126
8127 stop_dnsmasq()
8128 stop_networkd()
8129
8130 output = check_output('ip address show dev veth99 scope global')
8131 print(output)
8132 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8133
8134 start_networkd()
8135 self.wait_online('veth-peer:routable')
8136
8137 output = check_output('ip address show dev veth99 scope global')
8138 print(output)
8139 self.assertNotIn('192.168.5.', output)
8140
8141 def test_dhcp_client_reuse_address_as_static(self):
8142 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
8143 start_networkd()
8144 self.wait_online('veth-peer:carrier')
8145 start_dnsmasq()
8146 self.wait_online('veth99:routable', 'veth-peer:routable')
8147
8148 # link become 'routable' when at least one protocol provide an valid address.
8149 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8150 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8151
8152 output = check_output('ip address show dev veth99 scope global')
8153 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
8154 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
8155 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
8156 print(static_network)
8157
8158 remove_network_unit('25-dhcp-client.network')
8159
8160 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
8161 f.write(static_network)
8162
8163 restart_networkd()
8164 self.wait_online('veth99:routable')
8165
8166 output = check_output('ip -4 address show dev veth99 scope global')
8167 print(output)
8168 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
8169 'valid_lft forever preferred_lft forever')
8170
8171 output = check_output('ip -6 address show dev veth99 scope global')
8172 print(output)
8173 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
8174 'valid_lft forever preferred_lft forever')
8175
8176 @expectedFailureIfModuleIsNotAvailable('vrf')
8177 def test_dhcp_client_vrf(self):
8178 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
8179 '25-vrf.netdev', '25-vrf.network')
8180 start_networkd()
8181 self.wait_online('veth-peer:carrier')
8182 start_dnsmasq()
8183 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
8184
8185 # link become 'routable' when at least one protocol provide an valid address.
8186 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8187 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8188
8189 print('## ip -d link show dev vrf99')
8190 output = check_output('ip -d link show dev vrf99')
8191 print(output)
8192 self.assertRegex(output, 'vrf table 42')
8193
8194 print('## ip address show vrf vrf99')
8195 output = check_output('ip address show vrf vrf99')
8196 print(output)
8197 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8198 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8199 self.assertRegex(output, 'inet6 .* scope link')
8200
8201 print('## ip address show dev veth99')
8202 output = check_output('ip address show dev veth99')
8203 print(output)
8204 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8205 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8206 self.assertRegex(output, 'inet6 .* scope link')
8207
8208 print('## ip route show vrf vrf99')
8209 output = check_output('ip route show vrf vrf99')
8210 print(output)
8211 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
8212 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
8213 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
8214
8215 print('## ip route show table main dev veth99')
8216 output = check_output('ip route show table main dev veth99')
8217 print(output)
8218 self.assertEqual(output, '')
8219
8220 def test_dhcp_client_gateway_onlink_implicit(self):
8221 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8222 '25-dhcp-client-gateway-onlink-implicit.network')
8223 start_networkd()
8224 self.wait_online('veth-peer:carrier')
8225 start_dnsmasq()
8226 self.wait_online('veth99:routable', 'veth-peer:routable')
8227
8228 output = networkctl_status('veth99')
8229 print(output)
8230 self.assertRegex(output, '192.168.5')
8231
8232 output = check_output('ip route list dev veth99 10.0.0.0/8')
8233 print(output)
8234 self.assertRegex(output, 'onlink')
8235 output = check_output('ip route list dev veth99 192.168.100.0/24')
8236 print(output)
8237 self.assertRegex(output, 'onlink')
8238
8239 def test_dhcp_client_with_ipv4ll(self):
8240 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8241 '25-dhcp-client-with-ipv4ll.network')
8242 start_networkd()
8243 # we need to increase timeout above default, as this will need to wait for
8244 # systemd-networkd to get the dhcpv4 transient failure event
8245 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
8246
8247 output = check_output('ip -4 address show dev veth99')
8248 print(output)
8249 self.assertNotIn('192.168.5.', output)
8250 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
8251
8252 start_dnsmasq()
8253 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
8254 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
8255 self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
8256 self.wait_online('veth99:routable')
8257
8258 output = check_output('ip -4 address show dev veth99')
8259 print(output)
8260 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
8261 self.assertNotIn('169.254.', output)
8262 self.assertNotIn('scope link', output)
8263
8264 stop_dnsmasq()
8265 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
8266 self.wait_address_dropped('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4', timeout_sec=130)
8267 self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
8268
8269 output = check_output('ip -4 address show dev veth99')
8270 print(output)
8271 self.assertNotIn('192.168.5.', output)
8272 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
8273
8274 def test_dhcp_client_use_dns(self):
8275 def check(self, ipv4, ipv6, needs_reconfigure=False):
8276 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8277 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8278 f.write('[DHCPv4]\nUseDNS=')
8279 f.write('yes' if ipv4 else 'no')
8280 f.write('\n[DHCPv6]\nUseDNS=')
8281 f.write('yes' if ipv6 else 'no')
8282 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8283
8284 networkctl_reload()
8285 if needs_reconfigure:
8286 networkctl_reconfigure('veth99')
8287 self.wait_online('veth99:routable')
8288
8289 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8290 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8291 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8292
8293 # make resolved re-read the link state file
8294 resolvectl('revert', 'veth99')
8295
8296 output = resolvectl('dns', 'veth99')
8297 print(output)
8298 if ipv4:
8299 self.assertIn('192.168.5.1', output)
8300 else:
8301 self.assertNotIn('192.168.5.1', output)
8302 if ipv6:
8303 self.assertIn('2600::1', output)
8304 else:
8305 self.assertNotIn('2600::1', output)
8306
8307 check_json(networkctl_json())
8308
8309 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8310
8311 start_networkd()
8312 self.wait_online('veth-peer:carrier')
8313 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8314 '--dhcp-option=option6:dns-server,[2600::1]')
8315
8316 check(self, True, True)
8317 check(self, True, False)
8318 check(self, False, True, needs_reconfigure=True)
8319 check(self, False, False)
8320
8321 def test_dhcp_client_default_use_domains(self):
8322 def check(self, common, ipv4, ipv6):
8323 mkdir_p(networkd_conf_dropin_dir)
8324 with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
8325 f.write('[Network]\nUseDomains=')
8326 f.write('yes\n' if common else 'no\n')
8327 f.write('[DHCPv4]\nUseDomains=')
8328 f.write('yes\n' if ipv4 else 'no\n')
8329 f.write('[DHCPv6]\nUseDomains=')
8330 f.write('yes\n' if ipv6 else 'no\n')
8331
8332 restart_networkd()
8333 self.wait_online('veth-peer:carrier')
8334 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8335 '--dhcp-option=option6:dns-server,[2600::1]',
8336 '--dhcp-option=option:domain-search,example.com',
8337 '--dhcp-option=option6:domain-search,example.com')
8338
8339 self.wait_online('veth99:routable')
8340
8341 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8342 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8343 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8344
8345 for _ in range(20):
8346 output = resolvectl('domain', 'veth99')
8347 if common or ipv4 or ipv6:
8348 if 'example.com' in output:
8349 break
8350 else:
8351 if 'example.com' not in output:
8352 break
8353 time.sleep(0.5)
8354 else:
8355 print(output)
8356 print(read_link_state_file('veth99'))
8357 self.fail('unexpected domain setting in resolved...')
8358
8359 stop_dnsmasq()
8360 remove_networkd_conf_dropin('default_use_domains.conf')
8361
8362 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8363 check(self, True, False, False)
8364 check(self, False, True, True)
8365 check(self, False, True, False)
8366 check(self, False, False, True)
8367 check(self, False, False, False)
8368
8369 def test_dhcp_client_use_dnr(self):
8370 def check(self, ipv4, ipv6, needs_reconfigure=False):
8371 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8372 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8373 f.write('[DHCPv4]\nUseDNS=')
8374 f.write('yes' if ipv4 else 'no')
8375 f.write('\n[DHCPv6]\nUseDNS=')
8376 f.write('yes' if ipv6 else 'no')
8377 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8378
8379 networkctl_reload()
8380 if needs_reconfigure:
8381 networkctl_reconfigure('veth99')
8382 self.wait_online('veth99:routable')
8383
8384 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8385 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8386 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8387
8388 # make resolved re-read the link state file
8389 resolvectl('revert', 'veth99')
8390
8391 output = resolvectl('dns', 'veth99')
8392 print(output)
8393 if ipv4:
8394 self.assertIn('8.8.8.8#dns.google', output)
8395 self.assertIn('0.7.4.2#homer.simpson', output)
8396 else:
8397 self.assertNotIn('8.8.8.8#dns.google', output)
8398 if ipv6:
8399 self.assertIn('2001:4860:4860::8888#dns.google', output)
8400 else:
8401 self.assertNotIn('2001:4860:4860::8888#dns.google', output)
8402
8403 check_json(networkctl_json())
8404
8405 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8406
8407 start_networkd()
8408 self.wait_online('veth-peer:carrier')
8409 dnr_v4 = dnr_v4_instance_data(adn = "dns.google", addrs = ["8.8.8.8", "8.8.4.4"])
8410 dnr_v4 += dnr_v4_instance_data(adn = "homer.simpson", addrs = ["0.7.4.2"], alpns = ("dot","h2","h3"), dohpath = "/springfield{?dns}")
8411 dnr_v6 = dnr_v6_instance_data(adn = "dns.google", addrs = ["2001:4860:4860::8888", "2001:4860:4860::8844"])
8412 masq = lambda bs: ':'.join(f"{b:02x}" for b in bs)
8413 start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}',
8414 f'--dhcp-option=option6:144,{masq(dnr_v6)}')
8415
8416 check(self, True, True)
8417 check(self, True, False)
8418 check(self, False, True, needs_reconfigure=True)
8419 check(self, False, False)
8420
8421 def test_dhcp_client_use_captive_portal(self):
8422 def check(self, ipv4, ipv6, needs_reconfigure=False):
8423 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8424 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8425 f.write('[DHCPv4]\nUseCaptivePortal=')
8426 f.write('yes' if ipv4 else 'no')
8427 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8428 f.write('yes' if ipv6 else 'no')
8429 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8430
8431 networkctl_reload()
8432 if needs_reconfigure:
8433 networkctl_reconfigure('veth99')
8434 self.wait_online('veth99:routable')
8435
8436 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8437 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8438 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8439
8440 output = networkctl_status('veth99')
8441 print(output)
8442 if ipv4 or ipv6:
8443 self.assertIn('Captive Portal: http://systemd.io', output)
8444 else:
8445 self.assertNotIn('Captive Portal: http://systemd.io', output)
8446
8447 check_json(networkctl_json())
8448
8449 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8450
8451 start_networkd()
8452 self.wait_online('veth-peer:carrier')
8453 start_dnsmasq('--dhcp-option=114,http://systemd.io',
8454 '--dhcp-option=option6:103,http://systemd.io')
8455
8456 check(self, True, True)
8457 check(self, True, False)
8458 check(self, False, True, needs_reconfigure=True)
8459 check(self, False, False)
8460
8461 def test_dhcp_client_reject_captive_portal(self):
8462 def check(self, ipv4, ipv6):
8463 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8464 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8465 f.write('[DHCPv4]\nUseCaptivePortal=')
8466 f.write('yes' if ipv4 else 'no')
8467 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8468 f.write('yes' if ipv6 else 'no')
8469 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8470
8471 networkctl_reload()
8472 self.wait_online('veth99:routable')
8473
8474 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8475 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8476 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8477
8478 output = networkctl_status('veth99')
8479 print(output)
8480 self.assertNotIn('Captive Portal: ', output)
8481 self.assertNotIn('invalid/url', output)
8482
8483 check_json(networkctl_json())
8484
8485 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8486
8487 start_networkd()
8488 self.wait_online('veth-peer:carrier')
8489 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
8490 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
8491 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
8492
8493 check(self, True, True)
8494 check(self, True, False)
8495 check(self, False, True)
8496 check(self, False, False)
8497
8498 class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
8499
8500 def setUp(self):
8501 setup_common()
8502
8503 def tearDown(self):
8504 tear_down_common()
8505
8506 def check_dhcp6_prefix(self, link):
8507 description = get_link_description(link)
8508
8509 self.assertIn('DHCPv6Client', description.keys())
8510 self.assertIn('Prefixes', description['DHCPv6Client'])
8511
8512 prefixInfo = description['DHCPv6Client']['Prefixes']
8513
8514 self.assertEqual(len(prefixInfo), 1)
8515
8516 self.assertIn('Prefix', prefixInfo[0].keys())
8517 self.assertIn('PrefixLength', prefixInfo[0].keys())
8518 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
8519 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
8520
8521 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
8522 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
8523 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
8524 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
8525
8526 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8527 def test_dhcp6pd_no_address(self):
8528 # For issue #29979.
8529 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
8530
8531 start_networkd()
8532 self.wait_online('veth-peer:routable')
8533 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
8534 self.wait_online('veth99:degraded')
8535
8536 print('### ip -6 address show dev veth99 scope global')
8537 output = check_output('ip -6 address show dev veth99 scope global')
8538 print(output)
8539 self.assertNotIn('inet6 3ffe:501:ffff', output)
8540
8541 print('### ip -6 route show dev lo')
8542 output = check_output('ip -6 route show dev lo')
8543 print(output)
8544 self.assertNotRegex(output, '3ffe:501:ffff:[2-9a-f]00::/56')
8545
8546 self.check_dhcp6_prefix('veth99')
8547
8548 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8549 def test_dhcp6pd_no_assign(self):
8550 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
8551 # However, the server does not provide IA_NA. For issue #31349.
8552 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
8553
8554 start_networkd()
8555 self.wait_online('veth-peer:routable')
8556 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
8557 self.wait_online('veth99:degraded')
8558
8559 print('### ip -6 address show dev veth99 scope global')
8560 output = check_output('ip -6 address show dev veth99 scope global')
8561 print(output)
8562 self.assertNotIn('inet6 3ffe:501:ffff', output)
8563
8564 print('### ip -6 route show type blackhole')
8565 output = check_output('ip -6 route show type blackhole')
8566 print(output)
8567 self.assertRegex(output, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8568
8569 self.check_dhcp6_prefix('veth99')
8570
8571 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8572 def test_dhcp6pd(self):
8573 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
8574 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8575 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8576 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8577 '25-dhcp-pd-downstream-dummy97.network',
8578 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8579 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
8580
8581 start_networkd()
8582 self.wait_online('veth-peer:routable')
8583 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
8584 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8585 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
8586
8587 self.setup_nftset('addr6', 'ipv6_addr')
8588 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
8589 self.setup_nftset('ifindex', 'iface_index')
8590
8591 # Check DBus assigned prefix information to veth99
8592 self.check_dhcp6_prefix('veth99')
8593
8594 print('### ip -6 address show dev veth-peer scope global')
8595 output = check_output('ip -6 address show dev veth-peer scope global')
8596 print(output)
8597 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
8598
8599 # Link Subnet IDs
8600 # test1: 0x00
8601 # dummy97: 0x01 (The link will appear later)
8602 # dummy98: 0x00
8603 # dummy99: auto -> 0x02 (No address assignment)
8604 # veth97: 0x08
8605 # veth98: 0x09
8606 # veth99: 0x10
8607
8608 print('### ip -6 address show dev veth99 scope global')
8609 output = check_output('ip -6 address show dev veth99 scope global')
8610 print(output)
8611 # IA_NA
8612 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8613 # address in IA_PD (Token=static)
8614 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
8615 # address in IA_PD (Token=eui64)
8616 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
8617 # address in IA_PD (temporary)
8618 # Note that the temporary addresses may appear after the link enters configured state
8619 self.wait_address('veth99', 'inet6 3ffe:501:ffff:[2-9a-f]10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8620
8621 print('### ip -6 address show dev test1 scope global')
8622 output = check_output('ip -6 address show dev test1 scope global')
8623 print(output)
8624 # address in IA_PD (Token=static)
8625 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8626 # address in IA_PD (temporary)
8627 self.wait_address('test1', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8628
8629 print('### ip -6 address show dev dummy98 scope global')
8630 output = check_output('ip -6 address show dev dummy98 scope global')
8631 print(output)
8632 # address in IA_PD (Token=static)
8633 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8634 # address in IA_PD (temporary)
8635 self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8636
8637 print('### ip -6 address show dev dummy99 scope global')
8638 output = check_output('ip -6 address show dev dummy99 scope global')
8639 print(output)
8640 # Assign=no
8641 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
8642
8643 print('### ip -6 address show dev veth97 scope global')
8644 output = check_output('ip -6 address show dev veth97 scope global')
8645 print(output)
8646 # address in IA_PD (Token=static)
8647 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8648 # address in IA_PD (Token=eui64)
8649 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8650 # address in IA_PD (temporary)
8651 self.wait_address('veth97', 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8652
8653 print('### ip -6 address show dev veth97-peer scope global')
8654 output = check_output('ip -6 address show dev veth97-peer scope global')
8655 print(output)
8656 # NDisc address (Token=static)
8657 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8658 # NDisc address (Token=eui64)
8659 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8660 # NDisc address (temporary)
8661 self.wait_address('veth97-peer', 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8662
8663 print('### ip -6 address show dev veth98 scope global')
8664 output = check_output('ip -6 address show dev veth98 scope global')
8665 print(output)
8666 # address in IA_PD (Token=static)
8667 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8668 # address in IA_PD (Token=eui64)
8669 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8670 # address in IA_PD (temporary)
8671 self.wait_address('veth98', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8672
8673 print('### ip -6 address show dev veth98-peer scope global')
8674 output = check_output('ip -6 address show dev veth98-peer scope global')
8675 print(output)
8676 # NDisc address (Token=static)
8677 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8678 # NDisc address (Token=eui64)
8679 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8680 # NDisc address (temporary)
8681 self.wait_address('veth98-peer', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8682
8683 print('### ip -6 route show type unreachable')
8684 output = check_output('ip -6 route show type unreachable')
8685 print(output)
8686 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8687
8688 print('### ip -6 route show dev veth99')
8689 output = check_output('ip -6 route show dev veth99')
8690 print(output)
8691 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
8692
8693 print('### ip -6 route show dev test1')
8694 output = check_output('ip -6 route show dev test1')
8695 print(output)
8696 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8697
8698 print('### ip -6 route show dev dummy98')
8699 output = check_output('ip -6 route show dev dummy98')
8700 print(output)
8701 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8702
8703 print('### ip -6 route show dev dummy99')
8704 output = check_output('ip -6 route show dev dummy99')
8705 print(output)
8706 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
8707
8708 print('### ip -6 route show dev veth97')
8709 output = check_output('ip -6 route show dev veth97')
8710 print(output)
8711 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
8712
8713 print('### ip -6 route show dev veth97-peer')
8714 output = check_output('ip -6 route show dev veth97-peer')
8715 print(output)
8716 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
8717
8718 print('### ip -6 route show dev veth98')
8719 output = check_output('ip -6 route show dev veth98')
8720 print(output)
8721 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
8722
8723 print('### ip -6 route show dev veth98-peer')
8724 output = check_output('ip -6 route show dev veth98-peer')
8725 print(output)
8726 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
8727
8728 # Test case for a downstream which appears later
8729 check_output('ip link add dummy97 type dummy')
8730 self.wait_online('dummy97:routable')
8731
8732 print('### ip -6 address show dev dummy97 scope global')
8733 output = check_output('ip -6 address show dev dummy97 scope global')
8734 print(output)
8735 # address in IA_PD (Token=static)
8736 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8737 # address in IA_PD (temporary)
8738 self.wait_address('dummy97', 'inet6 3ffe:501:ffff:[2-9a-f]01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8739
8740 print('### ip -6 route show dev dummy97')
8741 output = check_output('ip -6 route show dev dummy97')
8742 print(output)
8743 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
8744
8745 # Test case for reconfigure
8746 networkctl_reconfigure('dummy98', 'dummy99')
8747 self.wait_online('dummy98:routable', 'dummy99:degraded')
8748
8749 print('### ip -6 address show dev dummy98 scope global')
8750 output = check_output('ip -6 address show dev dummy98 scope global')
8751 print(output)
8752 # address in IA_PD (Token=static)
8753 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8754 # address in IA_PD (temporary)
8755 self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8756
8757 print('### ip -6 address show dev dummy99 scope global')
8758 output = check_output('ip -6 address show dev dummy99 scope global')
8759 print(output)
8760 # Assign=no
8761 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
8762
8763 print('### ip -6 route show dev dummy98')
8764 output = check_output('ip -6 route show dev dummy98')
8765 print(output)
8766 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8767
8768 print('### ip -6 route show dev dummy99')
8769 output = check_output('ip -6 route show dev dummy99')
8770 print(output)
8771 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
8772
8773 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
8774
8775 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
8776 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
8777 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
8778 self.check_nftset('ifindex', 'dummy98')
8779
8780 self.teardown_nftset('addr6', 'network6', 'ifindex')
8781
8782 def verify_dhcp4_6rd(self, tunnel_name, address_prefix, border_router):
8783 print('### ip -4 address show dev veth-peer scope global')
8784 output = check_output('ip -4 address show dev veth-peer scope global')
8785 print(output)
8786 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
8787
8788 # Link Subnet IDs
8789 # test1: 0x00
8790 # dummy97: 0x01 (The link will appear later)
8791 # dummy98: 0x00
8792 # dummy99: auto -> 0x0[23] (No address assignment)
8793 # 6rd-XXX: auto -> 0x0[23]
8794 # veth97: 0x08
8795 # veth98: 0x09
8796 # veth99: 0x10
8797
8798 print('### ip -4 address show dev veth99 scope global')
8799 output = check_output('ip -4 address show dev veth99 scope global')
8800 print(output)
8801 self.assertRegex(output, fr'inet {address_prefix}[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
8802
8803 print('### ip -6 address show dev veth99 scope global')
8804 output = check_output('ip -6 address show dev veth99 scope global')
8805 print(output)
8806 # address in IA_PD (Token=static)
8807 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8808 # address in IA_PD (Token=eui64)
8809 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
8810 # address in IA_PD (temporary)
8811 # Note that the temporary addresses may appear after the link enters configured state
8812 self.wait_address('veth99', 'inet6 2001:db8:6464:[0-9a-f]+10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8813
8814 print('### ip -6 address show dev test1 scope global')
8815 output = check_output('ip -6 address show dev test1 scope global')
8816 print(output)
8817 # address in IA_PD (Token=static)
8818 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8819 # address in IA_PD (temporary)
8820 self.wait_address('test1', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8821
8822 print('### ip -6 address show dev dummy98 scope global')
8823 output = check_output('ip -6 address show dev dummy98 scope global')
8824 print(output)
8825 # address in IA_PD (Token=static)
8826 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8827 # address in IA_PD (temporary)
8828 self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8829
8830 print('### ip -6 address show dev dummy99 scope global')
8831 output = check_output('ip -6 address show dev dummy99 scope global')
8832 print(output)
8833 # Assign=no
8834 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
8835
8836 print('### ip -6 address show dev veth97 scope global')
8837 output = check_output('ip -6 address show dev veth97 scope global')
8838 print(output)
8839 # address in IA_PD (Token=static)
8840 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8841 # address in IA_PD (Token=eui64)
8842 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8843 # address in IA_PD (temporary)
8844 self.wait_address('veth97', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8845
8846 print('### ip -6 address show dev veth97-peer scope global')
8847 output = check_output('ip -6 address show dev veth97-peer scope global')
8848 print(output)
8849 # NDisc address (Token=static)
8850 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8851 # NDisc address (Token=eui64)
8852 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8853 # NDisc address (temporary)
8854 self.wait_address('veth97-peer', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8855
8856 print('### ip -6 address show dev veth98 scope global')
8857 output = check_output('ip -6 address show dev veth98 scope global')
8858 print(output)
8859 # address in IA_PD (Token=static)
8860 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8861 # address in IA_PD (Token=eui64)
8862 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8863 # address in IA_PD (temporary)
8864 self.wait_address('veth98', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8865
8866 print('### ip -6 address show dev veth98-peer scope global')
8867 output = check_output('ip -6 address show dev veth98-peer scope global')
8868 print(output)
8869 # NDisc address (Token=static)
8870 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8871 # NDisc address (Token=eui64)
8872 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8873 # NDisc address (temporary)
8874 self.wait_address('veth98-peer', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8875
8876 print('### ip -6 route show type unreachable')
8877 output = check_output('ip -6 route show type unreachable')
8878 print(output)
8879 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
8880
8881 print('### ip -6 route show dev veth99')
8882 output = check_output('ip -6 route show dev veth99')
8883 print(output)
8884 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
8885
8886 print('### ip -6 route show dev test1')
8887 output = check_output('ip -6 route show dev test1')
8888 print(output)
8889 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
8890
8891 print('### ip -6 route show dev dummy98')
8892 output = check_output('ip -6 route show dev dummy98')
8893 print(output)
8894 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
8895
8896 print('### ip -6 route show dev dummy99')
8897 output = check_output('ip -6 route show dev dummy99')
8898 print(output)
8899 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
8900
8901 print('### ip -6 route show dev veth97')
8902 output = check_output('ip -6 route show dev veth97')
8903 print(output)
8904 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
8905
8906 print('### ip -6 route show dev veth97-peer')
8907 output = check_output('ip -6 route show dev veth97-peer')
8908 print(output)
8909 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
8910
8911 print('### ip -6 route show dev veth98')
8912 output = check_output('ip -6 route show dev veth98')
8913 print(output)
8914 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
8915
8916 print('### ip -6 route show dev veth98-peer')
8917 output = check_output('ip -6 route show dev veth98-peer')
8918 print(output)
8919 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
8920
8921 print('### ip -6 address show dev dummy97 scope global')
8922 output = check_output('ip -6 address show dev dummy97 scope global')
8923 print(output)
8924 # address in IA_PD (Token=static)
8925 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8926 # address in IA_PD (temporary)
8927 self.wait_address('dummy97', 'inet6 2001:db8:6464:[0-9a-f]+01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
8928
8929 print('### ip -6 route show dev dummy97')
8930 output = check_output('ip -6 route show dev dummy97')
8931 print(output)
8932 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
8933
8934 print(f'### ip -d link show dev {tunnel_name}')
8935 output = check_output(f'ip -d link show dev {tunnel_name}')
8936 print(output)
8937 self.assertIn(f'link/sit {address_prefix}', output)
8938 self.assertIn(f'local {address_prefix}', output)
8939 self.assertIn('ttl 64', output)
8940 self.assertIn('6rd-prefix 2001:db8::/32', output)
8941 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
8942
8943 print(f'### ip -6 address show dev {tunnel_name}')
8944 output = check_output(f'ip -6 address show dev {tunnel_name}')
8945 print(output)
8946 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic')
8947 self.assertRegex(output, fr'inet6 ::{address_prefix}[0-9]+/96 scope global')
8948
8949 print(f'### ip -6 route show dev {tunnel_name}')
8950 output = check_output(f'ip -6 route show dev {tunnel_name}')
8951 print(output)
8952 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
8953 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
8954
8955 print('### ip -6 route show default')
8956 output = check_output('ip -6 route show default')
8957 print(output)
8958 self.assertIn('default', output)
8959 self.assertIn(f'via ::{border_router} dev {tunnel_name}', output)
8960
8961 def test_dhcp4_6rd(self):
8962 def get_dhcp_6rd_prefix(link):
8963 description = get_link_description(link)
8964
8965 self.assertIn('DHCPv4Client', description.keys())
8966 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
8967
8968 prefixInfo = description['DHCPv4Client']['6rdPrefix']
8969 self.assertIn('Prefix', prefixInfo.keys())
8970 self.assertIn('PrefixLength', prefixInfo.keys())
8971 self.assertIn('IPv4MaskLength', prefixInfo.keys())
8972 self.assertIn('BorderRouters', prefixInfo.keys())
8973
8974 return prefixInfo
8975
8976 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
8977 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8978 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8979 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8980 '25-dhcp-pd-downstream-dummy97.network',
8981 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8982 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
8983 '80-6rd-tunnel.network')
8984
8985 start_networkd()
8986 self.wait_online('veth-peer:routable')
8987
8988 # ipv4masklen: 8
8989 # 6rd-prefix: 2001:db8::/32
8990 # br-addresss: 10.0.0.1
8991
8992 start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01',
8993 ipv4_range='10.100.100.100,10.100.100.200',
8994 ipv4_router='10.0.0.1')
8995 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8996 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
8997
8998 # Check the DBus interface for assigned prefix information
8999 prefixInfo = get_dhcp_6rd_prefix('veth99')
9000
9001 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
9002 self.assertEqual(prefixInfo['PrefixLength'], 32)
9003 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
9004 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
9005
9006 # Test case for a downstream which appears later
9007 check_output('ip link add dummy97 type dummy')
9008 self.wait_online('dummy97:routable')
9009
9010 # Find tunnel name
9011 tunnel_name = None
9012 for name in os.listdir('/sys/class/net/'):
9013 if name.startswith('6rd-'):
9014 tunnel_name = name
9015 break
9016
9017 self.wait_online(f'{tunnel_name}:routable')
9018
9019 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
9020
9021 # Test case for reconfigure
9022 networkctl_reconfigure('dummy98', 'dummy99')
9023 self.wait_online('dummy98:routable', 'dummy99:degraded')
9024
9025 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
9026
9027 # Change the address range and (border) router, then if check the same tunnel is reused.
9028 stop_dnsmasq()
9029 start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:02',
9030 ipv4_range='10.100.100.200,10.100.100.250',
9031 ipv4_router='10.0.0.2')
9032
9033 print('Wait for the DHCP lease to be renewed/rebind')
9034 time.sleep(120)
9035
9036 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
9037 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
9038
9039 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.2', '10.0.0.2')
9040
9041 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
9042
9043 def setUp(self):
9044 setup_common()
9045
9046 def tearDown(self):
9047 tear_down_common()
9048
9049 def test_ipv6_route_prefix(self):
9050 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
9051 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9052
9053 start_networkd()
9054 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9055
9056 print('### ip -6 address show dev veth-peer')
9057 output = check_output('ip -6 address show dev veth-peer')
9058 print(output)
9059 self.assertIn('inet6 2001:db8:0:1:', output)
9060 self.assertNotIn('inet6 2001:db8:0:2:', output)
9061 self.assertNotIn('inet6 2001:db8:0:3:', output)
9062
9063 print('### ip -6 route show dev veth-peer')
9064 output = check_output('ip -6 route show dev veth-peer')
9065 print(output)
9066 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9067 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
9068 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
9069 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
9070 self.assertNotIn('2001:db1:fff::/64', output)
9071 self.assertNotIn('2001:db2:fff::/64', output)
9072
9073 print('### ip -6 nexthop show dev veth-peer')
9074 output = check_output('ip -6 nexthop show dev veth-peer')
9075 print(output)
9076 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9077
9078 print('### ip -6 address show dev veth99')
9079 output = check_output('ip -6 address show dev veth99')
9080 print(output)
9081 self.assertNotIn('inet6 2001:db8:0:1:', output)
9082 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
9083 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
9084 self.assertNotIn('inet6 2001:db8:0:3:', output)
9085
9086 output = resolvectl('dns', 'veth-peer')
9087 print(output)
9088 self.assertRegex(output, '2001:db8:1:1::2')
9089
9090 output = resolvectl('domain', 'veth-peer')
9091 print(output)
9092 self.assertIn('example.com', output)
9093
9094 check_json(networkctl_json())
9095
9096 output = networkctl_json('veth-peer')
9097 check_json(output)
9098
9099 # PREF64 or NAT64
9100 pref64 = json.loads(output)['NDisc']['PREF64'][0]
9101
9102 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
9103 self.assertEqual(prefix, '64:ff9b::')
9104
9105 prefix_length = pref64['PrefixLength']
9106 self.assertEqual(prefix_length, 96)
9107
9108 def test_ipv6_route_prefix_deny_list(self):
9109 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
9110 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9111
9112 start_networkd()
9113 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9114
9115 print('### ip -6 address show dev veth-peer')
9116 output = check_output('ip -6 address show dev veth-peer')
9117 print(output)
9118 self.assertIn('inet6 2001:db8:0:1:', output)
9119 self.assertNotIn('inet6 2001:db8:0:2:', output)
9120
9121 print('### ip -6 route show dev veth-peer')
9122 output = check_output('ip -6 route show dev veth-peer')
9123 print(output)
9124 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9125 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
9126 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
9127 self.assertNotIn('2001:db1:fff::/64', output)
9128
9129 print('### ip -6 nexthop show dev veth-peer')
9130 output = check_output('ip -6 nexthop show dev veth-peer')
9131 print(output)
9132 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9133
9134 print('### ip -6 address show dev veth99')
9135 output = check_output('ip -6 address show dev veth99')
9136 print(output)
9137 self.assertNotIn('inet6 2001:db8:0:1:', output)
9138 self.assertIn('inet6 2001:db8:0:2:', output)
9139
9140 output = resolvectl('dns', 'veth-peer')
9141 print(output)
9142 self.assertRegex(output, '2001:db8:1:1::2')
9143
9144 output = resolvectl('domain', 'veth-peer')
9145 print(output)
9146 self.assertIn('example.com', output)
9147
9148 class NetworkdMTUTests(unittest.TestCase, Utilities):
9149
9150 def setUp(self):
9151 setup_common()
9152
9153 def tearDown(self):
9154 tear_down_common()
9155
9156 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
9157 if not ipv6_mtu:
9158 ipv6_mtu = mtu
9159
9160 # test normal start
9161 start_networkd()
9162 self.wait_online('dummy98:routable')
9163 self.check_link_attr('dummy98', 'mtu', mtu)
9164 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
9165
9166 # test normal restart
9167 restart_networkd()
9168 self.wait_online('dummy98:routable')
9169 self.check_link_attr('dummy98', 'mtu', mtu)
9170 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
9171
9172 if reset:
9173 self.reset_check_mtu(mtu, ipv6_mtu)
9174
9175 def reset_check_mtu(self, mtu, ipv6_mtu=None):
9176 ''' test setting mtu/ipv6_mtu with interface already up '''
9177 stop_networkd()
9178
9179 # note - changing the device mtu resets the ipv6 mtu
9180 check_output('ip link set up mtu 1501 dev dummy98')
9181 check_output('ip link set up mtu 1500 dev dummy98')
9182 self.check_link_attr('dummy98', 'mtu', '1500')
9183 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
9184
9185 self.check_mtu(mtu, ipv6_mtu, reset=False)
9186
9187 def test_mtu_network(self):
9188 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
9189 self.check_mtu('1600')
9190
9191 def test_mtu_netdev(self):
9192 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
9193 # note - MTU set by .netdev happens ONLY at device creation!
9194 self.check_mtu('1600', reset=False)
9195
9196 def test_mtu_link(self):
9197 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
9198 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
9199 self.check_mtu('1600', reset=False)
9200
9201 def test_ipv6_mtu(self):
9202 ''' set ipv6 mtu without setting device mtu '''
9203 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
9204 self.check_mtu('1500', '1400')
9205
9206 def test_ipv6_mtu_toolarge(self):
9207 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
9208 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
9209 self.check_mtu('1500', '1500')
9210
9211 def test_mtu_network_ipv6_mtu(self):
9212 ''' set ipv6 mtu and set device mtu via network file '''
9213 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
9214 self.check_mtu('1600', '1550')
9215
9216 def test_mtu_netdev_ipv6_mtu(self):
9217 ''' set ipv6 mtu and set device mtu via netdev file '''
9218 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
9219 self.check_mtu('1600', '1550', reset=False)
9220
9221 def test_mtu_link_ipv6_mtu(self):
9222 ''' set ipv6 mtu and set device mtu via link file '''
9223 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
9224 self.check_mtu('1600', '1550', reset=False)
9225
9226 class NetworkdSysctlTest(unittest.TestCase, Utilities):
9227
9228 def setUp(self):
9229 setup_common()
9230
9231 def tearDown(self):
9232 tear_down_common()
9233
9234 @unittest.skipUnless(compare_kernel_version("6.12"), reason="On kernels <= 6.12, bpf_current_task_under_cgroup() isn't available for program types BPF_PROG_TYPE_CGROUP_SYSCTL")
9235 def check_sysctl_watch(self):
9236 copy_network_unit('12-dummy.network', '12-dummy.netdev', '12-dummy.link')
9237 start_networkd()
9238
9239 self.wait_online('dummy98:routable')
9240
9241 # Change managed sysctls
9242 call('sysctl -w net.ipv6.conf.dummy98.accept_ra=1')
9243 call('sysctl -w net.ipv6.conf.dummy98.mtu=1360')
9244 call('sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0')
9245 call('sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1')
9246
9247 # And unmanaged ones
9248 call('sysctl -w net.ipv6.conf.dummy98.hop_limit=4')
9249 call('sysctl -w net.ipv6.conf.dummy98.max_addresses=10')
9250
9251 log=read_networkd_log()
9252 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/accept_ra' from '0' to '1', conflicting with our setting to '0'")
9253 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/mtu' from '1550' to '1360', conflicting with our setting to '1550'")
9254 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv4/conf/dummy98/promote_secondaries' from '1' to '0', conflicting with our setting to '1'")
9255 self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/proxy_ndp' from '0' to '1', conflicting with our setting to '0'")
9256 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'", log)
9257 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log)
9258
9259 if __name__ == '__main__':
9260 parser = argparse.ArgumentParser()
9261 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
9262 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
9263 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
9264 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
9265 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
9266 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
9267 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
9268 parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage)
9269 parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false')
9270 ns, unknown_args = parser.parse_known_args(namespace=unittest)
9271
9272 if ns.build_dir:
9273 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
9274 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
9275 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
9276 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
9277 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
9278 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
9279 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
9280 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
9281 build_dir = ns.build_dir
9282
9283 if ns.source_dir:
9284 source_dir = ns.source_dir
9285 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
9286 elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))):
9287 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
9288 else:
9289 source_dir = None
9290
9291 if networkd_bin is None or resolved_bin is None or timesyncd_bin is None:
9292 print("networkd tests require networkd/resolved/timesyncd to be enabled")
9293 sys.exit(77)
9294
9295 use_valgrind = ns.use_valgrind
9296 enable_debug = ns.enable_debug
9297 asan_options = ns.asan_options
9298 lsan_options = ns.lsan_options
9299 ubsan_options = ns.ubsan_options
9300 with_coverage = ns.with_coverage or "COVERAGE_BUILD_DIR" in os.environ
9301 show_journal = ns.show_journal
9302
9303 if use_valgrind:
9304 # Do not forget the trailing space.
9305 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
9306
9307 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
9308 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
9309 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
9310 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
9311 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
9312
9313 if build_dir:
9314 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
9315 else:
9316 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
9317
9318 if asan_options:
9319 env.update({'ASAN_OPTIONS': asan_options})
9320 if lsan_options:
9321 env.update({'LSAN_OPTIONS': lsan_options})
9322 if ubsan_options:
9323 env.update({'UBSAN_OPTIONS': ubsan_options})
9324 if use_valgrind:
9325 env.update({'SYSTEMD_MEMPOOL': '0'})
9326
9327 wait_online_env = env.copy()
9328 if enable_debug:
9329 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
9330
9331 unittest.main(
9332 verbosity=3,
9333 argv=[
9334 sys.argv[0],
9335 *unknown_args,
9336 *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else [])
9337 ],
9338 )