]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
dc867cd1d47da9e026177da9a6988a64f01b9618
[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 expectedFailureIfAlternativeNameIsNotAvailable():
243 def f(func):
244 call_quiet('ip link add dummy98 type dummy')
245 supported = \
246 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
247 call_quiet('ip link show dev hogehogehogehogehoge') == 0
248 remove_link('dummy98')
249 return func if supported else unittest.expectedFailure(func)
250
251 return f
252
253 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
254 def f(func):
255 def finalize(func, supported):
256 call_quiet('rmmod netdevsim')
257 return func if supported else unittest.expectedFailure(func)
258
259 call_quiet('rmmod netdevsim')
260 if call_quiet('modprobe netdevsim') != 0:
261 return finalize(func, False)
262
263 try:
264 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
265 f.write('99 1')
266 except OSError:
267 return finalize(func, False)
268
269 return finalize(func, os.path.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
270
271 return f
272
273 def expectedFailureIfKernelReturnsInvalidFlags():
274 '''
275 This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
276 fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
277 '''
278 def f(func):
279 call_quiet('ip link add dummy98 type dummy')
280 call_quiet('ip link set up dev dummy98')
281 call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute')
282 output = check_output('ip address show dev dummy98')
283 remove_link('dummy98')
284 return func if 'noprefixroute' in output else unittest.expectedFailure(func)
285
286 return f
287
288 # pylint: disable=C0415
289 def compare_kernel_version(min_kernel_version):
290 try:
291 import platform
292 from packaging import version
293 except ImportError:
294 print('Failed to import either platform or packaging module, assuming the comparison failed')
295 return False
296
297 # Get only the actual kernel version without any build/distro/arch stuff
298 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
299 kver = platform.release().split('-')[0]
300 # Get also rid of '+'
301 kver = kver.split('+')[0]
302
303 return version.parse(kver) >= version.parse(min_kernel_version)
304
305 def copy_network_unit(*units, copy_dropins=True):
306 """
307 Copy networkd unit files into the testbed.
308
309 Any networkd unit file type can be specified, as well as drop-in files.
310
311 By default, all drop-ins for a specified unit file are copied in;
312 to avoid that specify dropins=False.
313
314 When a drop-in file is specified, its unit file is also copied in automatically.
315 """
316 has_link = False
317 mkdir_p(network_unit_dir)
318 for unit in units:
319 if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
320 cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
321
322 if unit.endswith('.conf'):
323 dropin = unit
324 unit = os.path.dirname(dropin).rstrip('.d')
325 dropindir = os.path.join(network_unit_dir, unit + '.d')
326 mkdir_p(dropindir)
327 cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
328
329 cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
330
331 if unit.endswith('.link'):
332 has_link = True
333
334 if has_link:
335 udevadm_reload()
336
337 def copy_credential(src, target):
338 mkdir_p(credstore_dir)
339 cp(os.path.join(networkd_ci_temp_dir, src),
340 os.path.join(credstore_dir, target))
341
342 def remove_network_unit(*units):
343 """
344 Remove previously copied unit files from the testbed.
345
346 Drop-ins will be removed automatically.
347 """
348 has_link = False
349 for unit in units:
350 rm_f(os.path.join(network_unit_dir, unit))
351 rm_rf(os.path.join(network_unit_dir, unit + '.d'))
352
353 if unit.endswith('.link') or unit.endswith('.link.d'):
354 has_link = True
355
356 if has_link:
357 udevadm_reload()
358
359 def touch_network_unit(*units):
360 for unit in units:
361 touch(os.path.join(network_unit_dir, unit))
362
363 def clear_network_units():
364 has_link = False
365 if os.path.exists(network_unit_dir):
366 units = os.listdir(network_unit_dir)
367 for unit in units:
368 if unit.endswith('.link') or unit.endswith('.link.d'):
369 has_link = True
370
371 rm_rf(network_unit_dir)
372
373 if has_link:
374 udevadm_reload()
375
376 def copy_networkd_conf_dropin(*dropins):
377 """Copy networkd.conf dropin files into the testbed."""
378 mkdir_p(networkd_conf_dropin_dir)
379 for dropin in dropins:
380 cp(os.path.join(networkd_ci_temp_dir, dropin), networkd_conf_dropin_dir)
381
382 def remove_networkd_conf_dropin(*dropins):
383 """Remove previously copied networkd.conf dropin files from the testbed."""
384 for dropin in dropins:
385 rm_f(os.path.join(networkd_conf_dropin_dir, dropin))
386
387 def clear_networkd_conf_dropins():
388 rm_rf(networkd_conf_dropin_dir)
389
390 def setup_systemd_udev_rules():
391 if not build_dir and not source_dir:
392 return
393
394 mkdir_p(udev_rules_dir)
395
396 for path in [build_dir, source_dir]:
397 if not path:
398 continue
399
400 path = os.path.join(path, "rules.d")
401 print(f"Copying udev rules from {path} to {udev_rules_dir}")
402
403 for rule in os.listdir(path):
404 if not rule.endswith(".rules"):
405 continue
406 cp(os.path.join(path, rule), udev_rules_dir)
407
408 def clear_networkd_state_files():
409 rm_rf('/var/lib/systemd/network/')
410
411 def copy_udev_rule(*rules):
412 """Copy udev rules"""
413 mkdir_p(udev_rules_dir)
414 for rule in rules:
415 cp(os.path.join(networkd_ci_temp_dir, rule), udev_rules_dir)
416
417 def remove_udev_rule(*rules):
418 """Remove previously copied udev rules"""
419 for rule in rules:
420 rm_f(os.path.join(udev_rules_dir, rule))
421
422 def clear_udev_rules():
423 rm_rf(udev_rules_dir)
424
425 def save_active_units():
426 for u in ['systemd-networkd.socket', 'systemd-networkd.service',
427 'systemd-resolved.service', 'systemd-timesyncd.service',
428 'firewalld.service']:
429 if call(f'systemctl is-active --quiet {u}') == 0:
430 call(f'systemctl stop {u}')
431 active_units.append(u)
432
433 def restore_active_units():
434 if 'systemd-networkd.socket' in active_units:
435 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
436 for u in active_units:
437 call(f'systemctl restart {u}')
438
439 def create_unit_dropin(unit, contents):
440 mkdir_p(f'/run/systemd/system/{unit}.d')
441 with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
442 f.write('\n'.join(contents))
443
444 def create_service_dropin(service, command, additional_settings=None):
445 drop_in = ['[Service]']
446 if command:
447 drop_in += [
448 'ExecStart=',
449 f'ExecStart={valgrind_cmd}{command}',
450 ]
451 if enable_debug:
452 drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
453 if asan_options:
454 drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
455 if lsan_options:
456 drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
457 if ubsan_options:
458 drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
459 if asan_options or lsan_options or ubsan_options:
460 drop_in += ['SystemCallFilter=']
461 if use_valgrind or asan_options or lsan_options or ubsan_options:
462 drop_in += ['MemoryDenyWriteExecute=no']
463 if use_valgrind:
464 drop_in += [
465 'Environment=SYSTEMD_MEMPOOL=0',
466 'PrivateTmp=yes',
467 ]
468 if with_coverage:
469 drop_in += [
470 'ProtectSystem=no',
471 'ProtectHome=no',
472 ]
473 if additional_settings:
474 drop_in += additional_settings
475
476 create_unit_dropin(f'{service}.service', drop_in)
477
478 def setup_system_units():
479 if build_dir or source_dir:
480 mkdir_p('/run/systemd/system/')
481
482 for unit in [
483 'systemd-networkd.service',
484 'systemd-networkd.socket',
485 'systemd-networkd-persistent-storage.service',
486 'systemd-resolved.service',
487 'systemd-timesyncd.service',
488 'systemd-udevd.service',
489 ]:
490 for path in [build_dir, source_dir]:
491 if not path:
492 continue
493
494 fullpath = os.path.join(os.path.join(path, "units"), unit)
495 if os.path.exists(fullpath):
496 print(f"Copying unit file from {fullpath} to /run/systemd/system/")
497 cp(fullpath, '/run/systemd/system/')
498 break
499
500 create_service_dropin('systemd-networkd', networkd_bin,
501 ['[Service]',
502 'Restart=no',
503 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
504 '[Unit]',
505 'StartLimitIntervalSec=0'])
506 create_service_dropin('systemd-resolved', resolved_bin)
507 create_service_dropin('systemd-timesyncd', timesyncd_bin)
508
509 # TODO: also run udevd with sanitizers, valgrind, or coverage
510 create_unit_dropin(
511 'systemd-udevd.service',
512 [
513 '[Service]',
514 'ExecStart=',
515 f'ExecStart=@{udevadm_bin} systemd-udevd',
516 ]
517 )
518 create_unit_dropin(
519 'systemd-networkd.socket',
520 [
521 '[Unit]',
522 'StartLimitIntervalSec=0',
523 ]
524 )
525 create_unit_dropin(
526 'systemd-networkd-persistent-storage.service',
527 [
528 '[Unit]',
529 'StartLimitIntervalSec=0',
530 '[Service]',
531 'ExecStart=',
532 f'ExecStart={networkctl_bin} persistent-storage yes',
533 'ExecStop=',
534 f'ExecStop={networkctl_bin} persistent-storage no',
535 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '',
536 ]
537 )
538
539 check_output('systemctl daemon-reload')
540 print(check_output('systemctl cat systemd-networkd.service'))
541 print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
542 print(check_output('systemctl cat systemd-resolved.service'))
543 print(check_output('systemctl cat systemd-timesyncd.service'))
544 print(check_output('systemctl cat systemd-udevd.service'))
545 check_output('systemctl restart systemd-resolved.service')
546 check_output('systemctl restart systemd-timesyncd.service')
547 check_output('systemctl restart systemd-udevd.service')
548
549 def clear_system_units():
550 def rm_unit(name):
551 rm_f(f'/run/systemd/system/{name}')
552 rm_rf(f'/run/systemd/system/{name}.d')
553
554 rm_unit('systemd-networkd.service')
555 rm_unit('systemd-networkd.socket')
556 rm_unit('systemd-networkd-persistent-storage.service')
557 rm_unit('systemd-resolved.service')
558 rm_unit('systemd-timesyncd.service')
559 rm_unit('systemd-udevd.service')
560 check_output('systemctl daemon-reload')
561 check_output('systemctl restart systemd-udevd.service')
562
563 def link_exists(*links):
564 for link in links:
565 if call_quiet(f'ip link show {link}') != 0:
566 return False
567 return True
568
569 def link_resolve(link):
570 return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0]
571
572 def remove_link(*links, protect=False):
573 for link in links:
574 if protect and link in protected_links:
575 continue
576 if link_exists(link):
577 call(f'ip link del dev {link}')
578
579 def save_existing_links():
580 links = os.listdir('/sys/class/net')
581 for link in links:
582 if link_exists(link):
583 protected_links.add(link)
584
585 print('### The following links will be protected:')
586 print(', '.join(sorted(list(protected_links))))
587
588 def unmanage_existing_links():
589 mkdir_p(network_unit_dir)
590
591 with open(os.path.join(network_unit_dir, '00-unmanaged.network'), mode='w', encoding='utf-8') as f:
592 f.write('[Match]\n')
593 for link in protected_links:
594 f.write(f'Name={link}\n')
595 f.write('\n[Link]\nUnmanaged=yes\n')
596
597 def flush_links():
598 links = os.listdir('/sys/class/net')
599 remove_link(*links, protect=True)
600
601 def flush_nexthops():
602 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
603 # Hence, we cannot restore nexthops in a simple way.
604 # Let's assume there is no nexthop used in the system
605 call_quiet('ip nexthop flush')
606
607 def save_routes():
608 # pylint: disable=global-statement
609 global saved_routes
610 saved_routes = check_output('ip route show table all')
611 print('### The following routes will be protected:')
612 print(saved_routes)
613
614 def flush_routes():
615 have = False
616 output = check_output('ip route show table all')
617 for line in output.splitlines():
618 if line in saved_routes:
619 continue
620 if 'proto kernel' in line:
621 continue
622 if ' dev ' in line and not ' dev lo ' in line:
623 continue
624 if not have:
625 have = True
626 print('### Removing routes that did not exist when the test started.')
627 print(f'# {line}')
628 call(f'ip route del {line}')
629
630 def save_routing_policy_rules():
631 # pylint: disable=global-statement
632 global saved_ipv4_rules, saved_ipv6_rules
633 def save(ipv):
634 output = check_output(f'ip -{ipv} rule show')
635 print(f'### The following IPv{ipv} routing policy rules will be protected:')
636 print(output)
637 return output
638
639 saved_ipv4_rules = save(4)
640 saved_ipv6_rules = save(6)
641
642 def flush_routing_policy_rules():
643 def flush(ipv, saved_rules):
644 have = False
645 output = check_output(f'ip -{ipv} rule show')
646 for line in output.splitlines():
647 if line in saved_rules:
648 continue
649 if not have:
650 have = True
651 print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
652 print(f'# {line}')
653 words = line.replace('lookup [l3mdev-table]', 'l3mdev').replace('[detached]', '').split()
654 priority = words[0].rstrip(':')
655 call(f'ip -{ipv} rule del priority {priority} ' + ' '.join(words[1:]))
656
657 flush(4, saved_ipv4_rules)
658 flush(6, saved_ipv6_rules)
659
660 def flush_fou_ports():
661 ret = run('ip fou show')
662 if ret.returncode != 0:
663 return # fou may not be supported
664 for line in ret.stdout.splitlines():
665 port = line.split()[1]
666 call(f'ip fou del port {port}')
667
668 def flush_l2tp_tunnels():
669 tids = []
670 ret = run('ip l2tp show tunnel')
671 if ret.returncode != 0:
672 return # l2tp may not be supported
673 for line in ret.stdout.splitlines():
674 words = line.split()
675 if words[0] == 'Tunnel':
676 tid = words[1].rstrip(',')
677 call(f'ip l2tp del tunnel tunnel_id {tid}')
678 tids.append(tid)
679
680 # Removing L2TP tunnel is asynchronous and slightly takes a time.
681 for tid in tids:
682 for _ in range(50):
683 r = run(f'ip l2tp show tunnel tunnel_id {tid}')
684 if r.returncode != 0 or len(r.stdout.rstrip()) == 0:
685 break
686 time.sleep(.2)
687 else:
688 print(f'Cannot remove L2TP tunnel {tid}, ignoring.')
689
690 def save_timezone():
691 # pylint: disable=global-statement
692 global saved_timezone
693 r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
694 if r.returncode == 0:
695 saved_timezone = r.stdout.rstrip()
696 print(f'### Saved timezone: {saved_timezone}')
697
698 def restore_timezone():
699 if saved_timezone:
700 call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
701
702 def read_link_attr(*args):
703 with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
704 return f.readline().strip()
705
706 def read_manager_state_file():
707 with open('/run/systemd/netif/state', encoding='utf-8') as f:
708 return f.read()
709
710 def read_link_state_file(link):
711 ifindex = read_link_attr(link, 'ifindex')
712 path = os.path.join('/run/systemd/netif/links', ifindex)
713 with open(path, encoding='utf-8') as f:
714 return f.read()
715
716 def read_ip_sysctl_attr(link, attribute, ipv):
717 with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f:
718 return f.readline().strip()
719
720 def read_ip_neigh_sysctl_attr(link, attribute, ipv):
721 with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f:
722 return f.readline().strip()
723
724 def read_ipv6_sysctl_attr(link, attribute):
725 return read_ip_sysctl_attr(link, attribute, 'ipv6')
726
727 def read_ipv6_neigh_sysctl_attr(link, attribute):
728 return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6')
729
730 def read_ipv4_sysctl_attr(link, attribute):
731 return read_ip_sysctl_attr(link, attribute, 'ipv4')
732
733 def read_mpls_sysctl_attr(link, attribute):
734 return read_ip_sysctl_attr(link, attribute, 'mpls')
735
736 def stop_by_pid_file(pid_file):
737 if not os.path.exists(pid_file):
738 return
739 with open(pid_file, 'r', encoding='utf-8') as f:
740 pid = f.read().rstrip(' \t\r\n\0')
741 os.kill(int(pid), signal.SIGTERM)
742 for _ in range(25):
743 try:
744 os.kill(int(pid), 0)
745 print(f"PID {pid} is still alive, waiting...")
746 time.sleep(.2)
747 except OSError as e:
748 if e.errno == errno.ESRCH:
749 break
750 print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
751 rm_f(pid_file)
752
753 def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
754 b = bytes()
755 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
756 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
757 ipv4 = ipaddress.IPv4Address
758 class SvcParam(enum.Enum):
759 ALPN = 1
760 DOHPATH = 7
761
762 data = pyton(prio)
763
764 adn = adn.rstrip('.') + '.'
765 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')))
766
767 if not addrs: # adn-only mode
768 return pack(data, 2)
769
770 data += pack(b.join(ipv4(addr).packed for addr in addrs))
771 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
772 if dohpath is not None:
773 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
774
775 return pack(data, 2)
776
777 def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
778 b = bytes()
779 pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
780 pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
781 ipv6 = ipaddress.IPv6Address
782 class SvcParam(enum.Enum):
783 ALPN = 1
784 DOHPATH = 7
785
786 data = pyton(prio)
787
788 adn = adn.rstrip('.') + '.'
789 data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')), 2)
790
791 if not addrs: # adn-only mode
792 return data
793
794 data += pack(b.join(ipv6(addr).packed for addr in addrs), 2)
795 data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
796 if dohpath is not None:
797 data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
798
799 return data
800
801 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'):
802 if ra_mode:
803 ra_mode = f',{ra_mode}'
804 else:
805 ra_mode = ''
806
807 command = (
808 'dnsmasq',
809 f'--log-facility={dnsmasq_log_file}',
810 '--log-queries=extra',
811 '--log-dhcp',
812 f'--pid-file={dnsmasq_pid_file}',
813 '--conf-file=/dev/null',
814 '--bind-interfaces',
815 f'--interface={interface}',
816 f'--dhcp-leasefile={dnsmasq_lease_file}',
817 '--enable-ra',
818 f'--dhcp-range={ipv6_range}{ra_mode},2m',
819 f'--dhcp-range={ipv4_range},2m',
820 '--dhcp-option=option:mtu,1492',
821 f'--dhcp-option=option:router,{ipv4_router}',
822 '--port=0',
823 '--no-resolv',
824 ) + additional_options
825 check_output(*command)
826
827 def stop_dnsmasq():
828 stop_by_pid_file(dnsmasq_pid_file)
829 rm_f(dnsmasq_lease_file)
830 rm_f(dnsmasq_log_file)
831
832 def read_dnsmasq_log_file():
833 with open(dnsmasq_log_file, encoding='utf-8') as f:
834 return f.read()
835
836 def start_isc_dhcpd(conf_file, ipv, interface='veth-peer'):
837 conf_file_path = os.path.join(networkd_ci_temp_dir, conf_file)
838 isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
839 touch(isc_dhcpd_lease_file)
840 check_output(isc_dhcpd_command)
841
842 def stop_isc_dhcpd():
843 stop_by_pid_file(isc_dhcpd_pid_file)
844 rm_f(isc_dhcpd_lease_file)
845
846 def get_dbus_link_path(link):
847 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
848 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
849 'GetLinkByName', 's', link])
850
851 assert out.startswith(b'io ')
852 out = out.strip()
853 assert out.endswith(b'"')
854 out = out.decode()
855 return out[:-1].split('"')[1]
856
857 def get_dhcp_client_state(link, family):
858 link_path = get_dbus_link_path(link)
859
860 out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
861 link_path, f'org.freedesktop.network1.DHCPv{family}Client', 'State'])
862 assert out.startswith(b's "')
863 out = out.strip()
864 assert out.endswith(b'"')
865 return out[3:-1].decode()
866
867 def get_dhcp4_client_state(link):
868 return get_dhcp_client_state(link, '4')
869
870 def get_dhcp6_client_state(link):
871 return get_dhcp_client_state(link, '6')
872
873 def get_link_description(link):
874 link_path = get_dbus_link_path(link)
875
876 out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1',
877 link_path, 'org.freedesktop.network1.Link', 'Describe'])
878 assert out.startswith(b's "')
879 out = out.strip()
880 assert out.endswith(b'"')
881 json_raw = out[2:].decode()
882 check_json(json_raw)
883 description = json.loads(json_raw) # Convert from escaped sequences to json
884 check_json(description)
885 return json.loads(description) # Now parse the json
886
887 def start_radvd(*additional_options, config_file):
888 config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file)
889 command = (
890 'radvd',
891 f'--pidfile={radvd_pid_file}',
892 f'--config={config_file_path}',
893 '--logmethod=stderr',
894 ) + additional_options
895 check_output(*command)
896
897 def stop_radvd():
898 stop_by_pid_file(radvd_pid_file)
899
900 def radvd_check_config(config_file):
901 if not shutil.which('radvd'):
902 print('radvd is not installed, assuming the config check failed')
903 return False
904
905 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
906 # set up (one instance is @unittest.skipX())
907 config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file)
908 return call(f'radvd --config={config_file_path} --configtest') == 0
909
910 def networkd_invocation_id():
911 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
912
913 def networkd_pid():
914 return check_output('systemctl show --value -p MainPID systemd-networkd.service')
915
916 def read_networkd_log(invocation_id=None, since=None):
917 if not invocation_id:
918 invocation_id = networkd_invocation_id()
919 command = [
920 'journalctl',
921 '--no-hostname',
922 '--output=short-monotonic',
923 f'_SYSTEMD_INVOCATION_ID={invocation_id}',
924 ]
925 if since:
926 command.append(f'--since={since}')
927 check_output('journalctl --sync')
928 return check_output(*command)
929
930 def networkd_is_failed():
931 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
932
933 def stop_networkd(show_logs=True, check_failed=True):
934 global show_journal
935 show_logs = show_logs and show_journal
936 if show_logs:
937 invocation_id = networkd_invocation_id()
938
939 if check_failed:
940 check_output('systemctl stop systemd-networkd.socket')
941 check_output('systemctl stop systemd-networkd.service')
942 else:
943 call('systemctl stop systemd-networkd.socket')
944 call('systemctl stop systemd-networkd.service')
945
946 if show_logs:
947 print(read_networkd_log(invocation_id))
948
949 # Check if networkd exits cleanly.
950 if check_failed:
951 assert not networkd_is_failed()
952
953 def start_networkd():
954 check_output('systemctl start systemd-networkd')
955 invocation_id = networkd_invocation_id()
956 pid = networkd_pid()
957 print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
958
959 def restart_networkd(show_logs=True):
960 global show_journal
961 show_logs = show_logs and show_journal
962 if show_logs:
963 invocation_id = networkd_invocation_id()
964 check_output('systemctl restart systemd-networkd.service')
965 if show_logs:
966 print(read_networkd_log(invocation_id))
967
968 invocation_id = networkd_invocation_id()
969 pid = networkd_pid()
970 print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
971
972 def networkd_pid():
973 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
974
975 def networkctl(*args):
976 # Do not call networkctl if networkd is in failed state.
977 # Otherwise, networkd may be restarted and we may get wrong results.
978 assert not networkd_is_failed()
979 return check_output(*(networkctl_cmd + list(args)), env=env)
980
981 def networkctl_status(*args):
982 return networkctl('-n', '0', 'status', *args)
983
984 def networkctl_json(*args):
985 return networkctl('--json=short', 'status', *args)
986
987 def networkctl_reconfigure(*links):
988 networkctl('reconfigure', *links)
989
990 def networkctl_reload():
991 networkctl('reload')
992
993 def resolvectl(*args):
994 return check_output(*(resolvectl_cmd + list(args)), env=env)
995
996 def timedatectl(*args):
997 return check_output(*(timedatectl_cmd + list(args)), env=env)
998
999 def udevadm(*args):
1000 return check_output(*(udevadm_cmd + list(args)))
1001
1002 def udevadm_reload():
1003 udevadm('control', '--reload')
1004
1005 def udevadm_trigger(*args, action='add'):
1006 udevadm('trigger', '--settle', f'--action={action}', *args)
1007
1008 def setup_common():
1009 # Protect existing links
1010 unmanage_existing_links()
1011
1012 # We usually show something in each test. So, let's break line to make the title of a test and output
1013 # from the test mixed. Then, flush stream buffer and journals.
1014 print()
1015 sys.stdout.flush()
1016 check_output('journalctl --sync')
1017
1018 def tear_down_common():
1019 # 1. stop DHCP/RA servers
1020 stop_dnsmasq()
1021 stop_isc_dhcpd()
1022 stop_radvd()
1023
1024 # 2. remove modules
1025 call_quiet('rmmod netdevsim')
1026 call_quiet('rmmod sch_teql')
1027
1028 # 3. remove network namespace
1029 call_quiet('ip netns del ns99')
1030
1031 # 4. remove links
1032 flush_l2tp_tunnels()
1033 flush_links()
1034
1035 # 5. stop networkd
1036 stop_networkd(check_failed=False)
1037
1038 # 6. remove configs
1039 clear_network_units()
1040 clear_networkd_conf_dropins()
1041 clear_networkd_state_files()
1042
1043 # 7. flush settings
1044 flush_fou_ports()
1045 flush_nexthops()
1046 flush_routing_policy_rules()
1047 flush_routes()
1048
1049 # 8. flush stream buffer and journals to make not any output from the test with the next one
1050 sys.stdout.flush()
1051 check_output('journalctl --sync')
1052
1053 # 9. check the status of networkd
1054 assert not networkd_is_failed()
1055
1056 def setUpModule():
1057 rm_rf(networkd_ci_temp_dir)
1058 cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir)
1059
1060 clear_network_units()
1061 clear_networkd_conf_dropins()
1062 clear_networkd_state_files()
1063 clear_udev_rules()
1064
1065 setup_systemd_udev_rules()
1066 copy_udev_rule('00-debug-net.rules')
1067
1068 # Save current state
1069 save_active_units()
1070 save_existing_links()
1071 save_routes()
1072 save_routing_policy_rules()
1073 save_timezone()
1074
1075 setup_system_units()
1076
1077 def tearDownModule():
1078 rm_rf(networkd_ci_temp_dir)
1079 clear_udev_rules()
1080 clear_network_units()
1081 clear_networkd_conf_dropins()
1082 clear_networkd_state_files()
1083
1084 restore_timezone()
1085
1086 clear_system_units()
1087 restore_active_units()
1088
1089 # Flush stream buffer and journals before showing the test summary.
1090 sys.stdout.flush()
1091 check_output('journalctl --sync')
1092
1093 class Utilities():
1094 # pylint: disable=no-member
1095
1096 def check_link_exists(self, *link, expected=True):
1097 if expected:
1098 self.assertTrue(link_exists(*link))
1099 else:
1100 self.assertFalse(link_exists(*link))
1101
1102 def check_link_attr(self, *args):
1103 self.assertEqual(read_link_attr(*args[:-1]), args[-1])
1104
1105 def check_bridge_port_attr(self, master, port, attribute, expected, allow_enoent=False):
1106 path = os.path.join('/sys/devices/virtual/net', master, 'lower_' + port, 'brport', attribute)
1107 if allow_enoent and not os.path.exists(path):
1108 return
1109 with open(path, encoding='utf-8') as f:
1110 self.assertEqual(f.readline().strip(), expected)
1111
1112 def check_ipv4_sysctl_attr(self, link, attribute, expected):
1113 self.assertEqual(read_ipv4_sysctl_attr(link, attribute), expected)
1114
1115 def check_ipv6_sysctl_attr(self, link, attribute, expected):
1116 self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected)
1117
1118 def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
1119 self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
1120
1121 def check_mpls_sysctl_attr(self, link, attribute, expected):
1122 self.assertEqual(read_mpls_sysctl_attr(link, attribute), expected)
1123
1124 def wait_links(self, *links, trial=40):
1125 for _ in range(trial):
1126 if link_exists(*links):
1127 break
1128 time.sleep(0.5)
1129 else:
1130 self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links)))
1131
1132 def wait_activated(self, link, state='down', trial=40):
1133 # wait for the interface is activated.
1134 needle = f'{link}: Bringing link {state}'
1135 flag = state.upper()
1136 self.wait_links(link, trial=trial)
1137 self.check_networkd_log(needle, trial=trial)
1138 for _ in range(trial):
1139 if flag in check_output(f'ip link show {link}'):
1140 break
1141 time.sleep(0.5)
1142 else:
1143 self.fail(f'Timed out waiting for {link} activated.')
1144
1145 def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
1146 """Wait for the link to reach the specified operstate and/or setup state.
1147
1148 Specify None or '' for either operstate or setup_state to ignore that state.
1149 This will recheck until the state conditions are met or the timeout expires.
1150
1151 If the link successfully matches the requested state, this returns True.
1152 If this times out waiting for the link to match, the behavior depends on the
1153 'fail_assert' parameter; if True, this causes a test assertion failure,
1154 otherwise this returns False. The default is to cause assertion failure.
1155
1156 Note that this function matches on *exactly* the given operstate and setup_state.
1157 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1158 """
1159 if not operstate:
1160 operstate = r'\S+'
1161 if not setup_state:
1162 setup_state = r'\S+'
1163
1164 for _ in range(setup_timeout * 2):
1165 if not link_exists(link):
1166 time.sleep(0.5)
1167 continue
1168 output = networkctl_status(link)
1169 if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
1170 return True
1171 time.sleep(0.5)
1172
1173 if fail_assert:
1174 self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1175 return False
1176
1177 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):
1178 """Wait for the links to reach the specified operstate and/or setup state.
1179
1180 This is similar to wait_operstate() but can be used for multiple links,
1181 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1182 The operstate should be specified in the link name, like 'eth0:degraded'.
1183 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1184
1185 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1186 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1187
1188 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1189 If this is set, no setup_state checks are done.
1190
1191 Set 'bool_dns' to True to wait for DNS servers to be accessible.
1192
1193 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1194 This is applied only for the operational state 'degraded' or above.
1195
1196 Note that this function waits for the links to reach *or exceed* the given operstate.
1197 However, the setup_state, if specified, must be matched *exactly*.
1198
1199 This returns if the links reached the requested operstate/setup_state; otherwise it
1200 raises CalledProcessError or fails test assertion.
1201 """
1202 args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + [f'--ignore={link}' for link in protected_links]
1203 if bool_any:
1204 args += ['--any']
1205 if bool_dns:
1206 args += ['--dns']
1207 if ipv4:
1208 args += ['--ipv4']
1209 if ipv6:
1210 args += ['--ipv6']
1211 try:
1212 check_output(*args, env=wait_online_env)
1213 except subprocess.CalledProcessError:
1214 if networkd_is_failed():
1215 print('!!!!! systemd-networkd.service is failed !!!!!')
1216 call('systemctl status systemd-networkd.service')
1217 else:
1218 # show detailed status on failure
1219 for link in links_with_operstate:
1220 name = link.split(':')[0]
1221 if link_exists(name):
1222 print(networkctl_status(name))
1223 else:
1224 print(f'Interface {name} not found.')
1225 raise
1226 if not bool_any and setup_state:
1227 for link in links_with_operstate:
1228 self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
1229
1230 def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
1231 for _ in range(timeout_sec * 2):
1232 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1233 if re.search(address_regex, output) and 'tentative' not in output:
1234 break
1235 time.sleep(0.5)
1236
1237 self.assertRegex(output, address_regex)
1238
1239 def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
1240 for _ in range(timeout_sec * 2):
1241 output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
1242 if not re.search(address_regex, output):
1243 break
1244 time.sleep(0.5)
1245
1246 self.assertNotRegex(output, address_regex)
1247
1248 def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1249 for _ in range(timeout_sec * 2):
1250 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1251 if re.search(route_regex, output):
1252 break
1253 time.sleep(0.5)
1254
1255 self.assertRegex(output, route_regex)
1256
1257 def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
1258 for _ in range(timeout_sec * 2):
1259 output = check_output(f'ip {ipv} route show dev {link} table {table}')
1260 if not re.search(route_regex, output):
1261 break
1262 time.sleep(0.5)
1263
1264 self.assertNotRegex(output, route_regex)
1265
1266 def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
1267 if not shutil.which('selinuxenabled'):
1268 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1269 elif call_quiet('selinuxenabled') != 0:
1270 print('## Checking NetLabel skipped: SELinux disabled.')
1271 elif not shutil.which('netlabelctl'): # not packaged by all distros
1272 print('## Checking NetLabel skipped: netlabelctl command not found.')
1273 else:
1274 output = check_output('netlabelctl unlbl list')
1275 print(output)
1276 self.assertRegex(output, f'interface:{interface},address:{address},label:"{label}"')
1277
1278 def setup_nftset(self, filter_name, filter_type, flags=''):
1279 if not shutil.which('nft'):
1280 print('## Setting up NFT sets skipped: nft command not found.')
1281 else:
1282 if call(f'nft add table inet sd_test') != 0:
1283 print('## Setting up NFT table failed.')
1284 self.fail()
1285 if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1286 print('## Setting up NFT sets failed.')
1287 self.fail()
1288
1289 def teardown_nftset(self, *filters):
1290 if not shutil.which('nft'):
1291 print('## Tearing down NFT sets skipped: nft command not found.')
1292 else:
1293 for filter_name in filters:
1294 if call(f'nft delete set inet sd_test {filter_name}') != 0:
1295 print('## Tearing down NFT sets failed.')
1296 self.fail()
1297 if call(f'nft delete table inet sd_test') != 0:
1298 print('## Tearing down NFT table failed.')
1299 self.fail()
1300
1301 def check_nftset(self, filter_name, contents):
1302 if not shutil.which('nft'):
1303 print('## Checking NFT sets skipped: nft command not found.')
1304 else:
1305 output = check_output(f'nft list set inet sd_test {filter_name}')
1306 print(output)
1307 self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
1308
1309 def check_networkd_log(self, contents, since=None, trial=20):
1310 for _ in range(trial):
1311 if contents in read_networkd_log(since=since):
1312 break
1313 time.sleep(0.5)
1314 else:
1315 self.fail(f'"{contents}" not found in journal.')
1316
1317 def networkctl_check_unit(self, ifname, netdev_file=None, network_file=None, link_file=None):
1318 output = networkctl_status(ifname)
1319 print(output)
1320 if netdev_file:
1321 self.assertRegex(output, rf'NetDev File: .*/{netdev_file}\.netdev')
1322 else:
1323 self.assertNotIn('NetDev File:', output)
1324 if network_file:
1325 self.assertRegex(output, rf'Network File: .*/{network_file}\.network')
1326 else:
1327 self.assertIn('Network File: n/a', output)
1328 if link_file:
1329 self.assertRegex(output, rf'Link File: .*/{link_file}\.link')
1330
1331 class NetworkctlTests(unittest.TestCase, Utilities):
1332
1333 def setUp(self):
1334 setup_common()
1335
1336 def tearDown(self):
1337 tear_down_common()
1338
1339 @expectedFailureIfAlternativeNameIsNotAvailable()
1340 def test_altname(self):
1341 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1342 start_networkd()
1343 self.wait_online('dummy98:degraded')
1344
1345 output = networkctl_status('dummy98')
1346 self.assertRegex(output, 'hogehogehogehogehogehoge')
1347
1348 @expectedFailureIfAlternativeNameIsNotAvailable()
1349 def test_rename_to_altname(self):
1350 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1351 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1352 start_networkd()
1353 self.wait_online('dummyalt:degraded')
1354
1355 output = networkctl_status('dummyalt')
1356 self.assertIn('hogehogehogehogehogehoge', output)
1357 self.assertNotIn('dummy98', output)
1358
1359 def test_reconfigure(self):
1360 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
1361 start_networkd()
1362 self.wait_online('dummy98:routable')
1363
1364 output = check_output('ip -4 address show dev dummy98')
1365 print(output)
1366 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1367 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1368 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1369
1370 check_output('ip address del 10.1.2.3/16 dev dummy98')
1371 check_output('ip address del 10.1.2.4/16 dev dummy98')
1372 check_output('ip address del 10.2.2.4/16 dev dummy98')
1373
1374 networkctl_reconfigure('dummy98')
1375 self.wait_online('dummy98:routable')
1376
1377 output = check_output('ip -4 address show dev dummy98')
1378 print(output)
1379 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1380 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1381 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1382
1383 remove_network_unit('25-address-static.network')
1384
1385 networkctl_reload()
1386 self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
1387
1388 output = check_output('ip -4 address show dev dummy98')
1389 print(output)
1390 self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1391 self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1392 self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1393
1394 copy_network_unit('25-address-static.network', copy_dropins=False)
1395 networkctl_reload()
1396 self.wait_online('dummy98:routable')
1397
1398 output = check_output('ip -4 address show dev dummy98')
1399 print(output)
1400 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
1401 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
1402 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
1403
1404 def test_renew(self):
1405 def check():
1406 self.wait_online('veth99:routable', 'veth-peer:routable')
1407 output = networkctl_status('veth99')
1408 print(output)
1409 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
1410 self.assertIn('Gateway: 192.168.5.3', output)
1411 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
1412 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
1413
1414 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1415 start_networkd()
1416 check()
1417 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1418
1419 for verb in ['renew', 'forcerenew']:
1420 networkctl(verb, 'veth99')
1421 check()
1422 networkctl(verb, 'veth99', 'veth99', 'veth99')
1423 check()
1424
1425 def test_up_down(self):
1426 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
1427 start_networkd()
1428 self.wait_online('dummy98:routable')
1429
1430 networkctl('down', 'dummy98')
1431 self.wait_online('dummy98:off')
1432 networkctl('up', 'dummy98')
1433 self.wait_online('dummy98:routable')
1434 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1435 self.wait_online('dummy98:off')
1436 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1437 self.wait_online('dummy98:routable')
1438
1439 def test_reload(self):
1440 start_networkd()
1441
1442 copy_network_unit('11-dummy.netdev')
1443 networkctl_reload()
1444 self.wait_operstate('test1', 'off', setup_state='unmanaged')
1445
1446 copy_network_unit('11-dummy.network')
1447 networkctl_reload()
1448 self.wait_online('test1:degraded')
1449
1450 remove_network_unit('11-dummy.network')
1451 networkctl_reload()
1452 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
1453
1454 remove_network_unit('11-dummy.netdev')
1455 networkctl_reload()
1456 self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
1457
1458 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1459 networkctl_reload()
1460 self.wait_operstate('test1', 'degraded')
1461
1462 def test_glob(self):
1463 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1464 start_networkd()
1465
1466 self.wait_online('test1:degraded')
1467
1468 output = networkctl('list')
1469 self.assertRegex(output, '1 lo ')
1470 self.assertRegex(output, 'test1')
1471
1472 output = networkctl('list', 'test1')
1473 self.assertNotRegex(output, '1 lo ')
1474 self.assertRegex(output, 'test1')
1475
1476 output = networkctl('list', 'te*')
1477 self.assertNotRegex(output, '1 lo ')
1478 self.assertRegex(output, 'test1')
1479
1480 output = networkctl_status('te*')
1481 self.assertNotRegex(output, '1: lo ')
1482 self.assertRegex(output, 'test1')
1483
1484 output = networkctl_status('tes[a-z][0-9]')
1485 self.assertNotRegex(output, '1: lo ')
1486 self.assertRegex(output, 'test1')
1487
1488 def test_mtu(self):
1489 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1490 start_networkd()
1491
1492 self.wait_online('test1:degraded')
1493
1494 output = networkctl_status('test1')
1495 self.assertRegex(output, 'MTU: 1600')
1496
1497 def test_type(self):
1498 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1499 start_networkd()
1500 self.wait_online('test1:degraded')
1501
1502 output = networkctl_status('test1')
1503 print(output)
1504 self.assertRegex(output, 'Type: ether')
1505
1506 output = networkctl_status('lo')
1507 print(output)
1508 self.assertRegex(output, 'Type: loopback')
1509
1510 def test_unit_file(self):
1511 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1512 start_networkd()
1513 self.wait_online('test1:degraded')
1514
1515 output = networkctl_status('test1')
1516 print(output)
1517 self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
1518 self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
1519 self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
1520 self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
1521
1522 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).')
1523
1524 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1525 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1526 # Let's reprocess the interface and drop the property.
1527 udevadm_trigger('/sys/class/net/lo')
1528 output = networkctl_status('lo')
1529 print(output)
1530 self.assertIn('Link File: n/a', output)
1531 self.assertIn('Network File: n/a', output)
1532
1533 def test_delete_links(self):
1534 copy_network_unit('11-dummy.netdev', '25-veth.netdev')
1535 start_networkd()
1536 self.wait_links('test1', 'veth99', 'veth-peer')
1537 networkctl('delete', 'test1', 'veth99')
1538 self.check_link_exists('test1', 'veth99', 'veth-peer', expected=False)
1539
1540 def test_label(self):
1541 networkctl('label')
1542
1543 class NetworkdMatchTests(unittest.TestCase, Utilities):
1544
1545 def setUp(self):
1546 setup_common()
1547
1548 def tearDown(self):
1549 tear_down_common()
1550
1551 @expectedFailureIfAlternativeNameIsNotAvailable()
1552 def test_match(self):
1553 copy_network_unit('12-dummy-mac.netdev',
1554 '12-dummy-match-mac-01.network',
1555 '12-dummy-match-mac-02.network',
1556 '12-dummy-match-renamed.network',
1557 '12-dummy-match-altname.network',
1558 '12-dummy-altname.link')
1559 start_networkd()
1560
1561 self.wait_online('dummy98:routable')
1562 output = networkctl_status('dummy98')
1563 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
1564 output = check_output('ip -4 address show dev dummy98')
1565 self.assertIn('10.0.0.1/16', output)
1566
1567 check_output('ip link set dev dummy98 down')
1568 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1569
1570 self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
1571 self.wait_online('dummy98:routable')
1572 output = networkctl_status('dummy98')
1573 self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
1574
1575 check_output('ip link set dev dummy98 down')
1576 check_output('ip link set dev dummy98 name dummy98-1')
1577
1578 self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
1579 self.wait_online('dummy98-1:routable')
1580 output = networkctl_status('dummy98-1')
1581 self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
1582
1583 check_output('ip link set dev dummy98-1 down')
1584 check_output('ip link set dev dummy98-1 name dummy98-2')
1585 udevadm_trigger('/sys/class/net/dummy98-2')
1586
1587 self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
1588 self.wait_online('dummy98-2:routable')
1589 output = networkctl_status('dummy98-2')
1590 self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
1591
1592 def test_match_udev_property(self):
1593 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1594 start_networkd()
1595 self.wait_online('dummy98:routable')
1596
1597 output = networkctl_status('dummy98')
1598 print(output)
1599 self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
1600
1601 class WaitOnlineTests(unittest.TestCase, Utilities):
1602
1603 def setUp(self):
1604 setup_common()
1605
1606 def tearDown(self):
1607 tear_down_common()
1608
1609 def test_wait_online_any(self):
1610 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1611 start_networkd()
1612
1613 self.wait_online('bridge99', 'test1:degraded', bool_any=True)
1614
1615 self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
1616 self.wait_operstate('test1', 'degraded')
1617
1618 def do_test_wait_online_dns(
1619 self,
1620 global_dns='',
1621 fallback_dns='',
1622 expect_timeout=False,
1623 network_dropin=None,
1624 ):
1625 global wait_online_env
1626
1627 if network_dropin is not None:
1628 network_dropin_path = os.path.join(
1629 network_unit_dir,
1630 '25-dhcp-client-use-dns-ipv4.network.d/test.conf'
1631 )
1632 mkdir_p(os.path.dirname(network_dropin_path))
1633 with open(network_dropin_path, 'w') as f:
1634 f.write(network_dropin)
1635
1636 copy_network_unit(
1637 '25-veth.netdev',
1638 '25-dhcp-client-use-dns-ipv4.network',
1639 '25-dhcp-server.network'
1640 )
1641 start_networkd()
1642 self.wait_online('veth-peer:routable')
1643
1644 # Unless given, clear global DNS configuration
1645 resolved_dropin = '/run/systemd/resolved.conf.d/global-dns.conf'
1646 mkdir_p(os.path.dirname(resolved_dropin))
1647 with open(resolved_dropin, 'w') as f:
1648 f.write((
1649 '[Resolve]\n'
1650 f'DNS={global_dns}\n'
1651 f'FallbackDNS={fallback_dns}\n'
1652 ))
1653 self.addCleanup(os.remove, resolved_dropin)
1654 check_output('systemctl reload systemd-resolved')
1655
1656 try:
1657 wait_online_env_copy = wait_online_env.copy()
1658
1659 wait_online_env['SYSTEMD_LOG_LEVEL'] = 'debug'
1660 wait_online_env['SYSTEMD_LOG_TARGET'] = 'console'
1661
1662 self.wait_online('veth99:routable', bool_dns=True)
1663
1664 if expect_timeout:
1665 # The above should have thrown an exception.
1666 self.fail(
1667 'Expected systemd-networkd-wait-online to time out'
1668 )
1669
1670 except subprocess.CalledProcessError as e:
1671 if expect_timeout:
1672 self.assertRegex(
1673 e.output,
1674 f'veth99: No link-specific DNS server is accessible',
1675 f'Missing expected log message:\n{e.output}'
1676 )
1677 else:
1678 self.fail(
1679 f'Command timed out:\n{e.output}'
1680 )
1681 finally:
1682 wait_online_env = wait_online_env_copy
1683
1684 def test_wait_online_dns(self):
1685 ''' test systemd-networkd-wait-online with --dns '''
1686 self.do_test_wait_online_dns()
1687
1688 def test_wait_online_dns_global(self):
1689 '''
1690 test systemd-networkd-wait-online with --dns, expect pass due to global DNS
1691 '''
1692
1693 # Set UseDNS=no, and allow global DNS to be used.
1694 self.do_test_wait_online_dns(
1695 global_dns='192.168.5.1',
1696 network_dropin=(
1697 '[DHCPv4]\n'
1698 'UseDNS=no\n'
1699 )
1700 )
1701
1702 def test_wait_online_dns_expect_timeout(self):
1703 ''' test systemd-networkd-wait-online with --dns, and expect timeout '''
1704
1705 # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
1706 self.do_test_wait_online_dns(
1707 expect_timeout=True,
1708 network_dropin=(
1709 '[Network]\n'
1710 'DNSDefaultRoute=yes\n'
1711 '[DHCPv4]\n'
1712 'UseDNS=no\n'
1713 )
1714 )
1715
1716 def test_wait_online_dns_expect_timeout_global(self):
1717 '''
1718 test systemd-networkd-wait-online with --dns, and expect timeout
1719 despite global DNS
1720 '''
1721
1722 # Configure Domains=~., and expect timeout despite global DNS servers
1723 # being available.
1724 self.do_test_wait_online_dns(
1725 expect_timeout=True,
1726 global_dns='192.168.5.1',
1727 network_dropin=(
1728 '[Network]\n'
1729 'Domains=~.\n'
1730 '[DHCPv4]\n'
1731 'UseDNS=no\n'
1732 )
1733 )
1734
1735
1736 class NetworkdNetDevTests(unittest.TestCase, Utilities):
1737
1738 def setUp(self):
1739 setup_common()
1740
1741 def tearDown(self):
1742 tear_down_common()
1743
1744 def test_dropin_and_name_conflict(self):
1745 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1746 start_networkd()
1747
1748 self.wait_online('dropin-test:off', setup_state='unmanaged')
1749
1750 output = check_output('ip link show dropin-test')
1751 print(output)
1752 self.assertRegex(output, '00:50:56:c0:00:28')
1753
1754 @expectedFailureIfModuleIsNotAvailable('bareudp')
1755 def test_bareudp(self):
1756 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1757 start_networkd()
1758
1759 self.wait_online('bareudp99:degraded')
1760 self.networkctl_check_unit('bareudp99', '25-bareudp', '26-netdev-link-local-addressing-yes')
1761
1762 output = check_output('ip -d link show bareudp99')
1763 print(output)
1764 self.assertRegex(output, 'dstport 1000 ')
1765 self.assertRegex(output, 'ethertype ip ')
1766 self.assertRegex(output, 'srcportmin 1001 ')
1767
1768 touch_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1769 networkctl_reload()
1770 self.wait_online('bareudp99:degraded')
1771
1772 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1773 def test_batadv(self):
1774 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1775 start_networkd()
1776
1777 self.wait_online('batadv99:degraded')
1778 self.networkctl_check_unit('batadv99', '25-batadv', '26-netdev-link-local-addressing-yes')
1779
1780 output = check_output('ip -d link show batadv99')
1781 print(output)
1782 self.assertRegex(output, 'batadv')
1783
1784 touch_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1785 networkctl_reload()
1786 self.wait_online('batadv99:degraded')
1787
1788 def test_bridge(self):
1789 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1790 start_networkd()
1791
1792 self.wait_online('bridge99:no-carrier')
1793 self.networkctl_check_unit('bridge99', '25-bridge', '25-bridge-configure-without-carrier')
1794
1795 tick = os.sysconf('SC_CLK_TCK')
1796 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
1797 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
1798 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick))
1799 self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick))
1800 self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1801 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1802 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1803 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1804 self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1805 self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn')))
1806
1807 output = networkctl_status('bridge99')
1808 print(output)
1809 self.assertRegex(output, 'Priority: 9')
1810 self.assertRegex(output, 'STP: yes')
1811 self.assertRegex(output, 'Multicast IGMP Version: 3')
1812 if 'FDB Max Learned' in output:
1813 self.assertRegex(output, 'FDB Max Learned: 4')
1814
1815 output = check_output('ip -d link show bridge99')
1816 print(output)
1817 self.assertIn('vlan_filtering 1 ', output)
1818 self.assertIn('vlan_protocol 802.1ad ', output)
1819 self.assertIn('vlan_default_pvid 9 ', output)
1820 if 'fdb_max_learned' in output:
1821 self.assertIn('fdb_max_learned 4 ', output)
1822
1823 def test_bond(self):
1824 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
1825 start_networkd()
1826
1827 self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged')
1828 self.networkctl_check_unit('bond99', '25-bond')
1829 self.networkctl_check_unit('bond98', '25-bond-balanced-tlb')
1830 self.networkctl_check_unit('bond97', '25-bond-property')
1831
1832 self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1833 self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1834 self.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1835 self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1836 self.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1837 self.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1838 self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1839 self.check_link_attr('bond99', 'bonding', 'min_links', '1')
1840 self.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1841 self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1842 self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1843
1844 self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1845 self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1846
1847 output = networkctl_status('bond99')
1848 print(output)
1849 self.assertIn('Mode: 802.3ad', output)
1850 self.assertIn('Miimon: 1s', output)
1851 self.assertIn('Updelay: 2s', output)
1852 self.assertIn('Downdelay: 2s', output)
1853
1854 output = networkctl_status('bond98')
1855 print(output)
1856 self.assertIn('Mode: balance-tlb', output)
1857
1858 output = networkctl_status('bond97')
1859 print(output)
1860
1861 self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
1862 self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
1863
1864 def check_vlan(self, id, flags):
1865 self.wait_online('test1:degraded', 'vlan99:routable')
1866 self.networkctl_check_unit('vlan99', '21-vlan', '21-vlan')
1867 self.networkctl_check_unit('test1', '11-dummy', '21-vlan-test1')
1868
1869 output = check_output('ip -d link show test1')
1870 print(output)
1871 self.assertRegex(output, ' mtu 2000 ')
1872
1873 output = check_output('ip -d link show vlan99')
1874 print(output)
1875 self.assertIn(' mtu 2000 ', output)
1876 if flags:
1877 self.assertIn('REORDER_HDR', output)
1878 self.assertIn('LOOSE_BINDING', output)
1879 self.assertIn('GVRP', output)
1880 self.assertIn('MVRP', output)
1881 else:
1882 self.assertNotIn('REORDER_HDR', output)
1883 self.assertNotIn('LOOSE_BINDING', output)
1884 self.assertNotIn('GVRP', output)
1885 self.assertNotIn('MVRP', output)
1886 self.assertIn(f' id {id} ', output)
1887 self.assertIn('ingress-qos-map { 4:100 7:13 }', output)
1888 self.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output)
1889
1890 output = check_output('ip -4 address show dev test1')
1891 print(output)
1892 self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1893 self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1894
1895 output = check_output('ip -4 address show dev vlan99')
1896 print(output)
1897 self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1898
1899 def test_vlan(self):
1900 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1901 '21-vlan.network', '21-vlan-test1.network')
1902 start_networkd()
1903 self.check_vlan(id=99, flags=True)
1904
1905 # Test for reloading .netdev file. See issue #34907.
1906 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1907 f.write('[VLAN]\nId=42\n')
1908
1909 # VLAN ID cannot be changed, so we need to remove the existing netdev.
1910 check_output("ip link del vlan99")
1911 networkctl_reload()
1912 self.check_vlan(id=42, flags=True)
1913
1914 with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f:
1915 f.write('[VLAN]\n'
1916 'GVRP=no\n'
1917 'MVRP=no\n'
1918 'LooseBinding=no\n'
1919 'ReorderHeader=no\n')
1920
1921 # flags can be changed, hence it is not necessary to remove the existing netdev.
1922 networkctl_reload()
1923 self.check_vlan(id=42, flags=False)
1924
1925 def test_vlan_on_bond(self):
1926 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1927 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1928
1929 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1930 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1931 start_networkd()
1932 self.wait_online('bond99:off')
1933 self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
1934 self.networkctl_check_unit('vlan99', '21-vlan-on-bond', '21-vlan-on-bond')
1935 self.networkctl_check_unit('bond99', '21-bond-802.3ad', '21-bond-802.3ad')
1936
1937 self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
1938
1939 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1940 networkctl_reload()
1941 self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1942
1943 def test_macvtap(self):
1944 first = True
1945 for mode in ['private', 'vepa', 'bridge', 'passthru']:
1946 if first:
1947 first = False
1948 else:
1949 self.tearDown()
1950
1951 print(f'### test_macvtap(mode={mode})')
1952 with self.subTest(mode=mode):
1953 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1954 '11-dummy.netdev', '25-macvtap.network')
1955 with open(os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8') as f:
1956 f.write('[MACVTAP]\nMode=' + mode)
1957 start_networkd()
1958
1959 self.wait_online('macvtap99:degraded',
1960 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1961 self.networkctl_check_unit('macvtap99', '21-macvtap', '26-netdev-link-local-addressing-yes')
1962 self.networkctl_check_unit('test1', '11-dummy', '25-macvtap')
1963
1964 output = check_output('ip -d link show macvtap99')
1965 print(output)
1966 self.assertRegex(output, 'macvtap mode ' + mode + ' ')
1967
1968 touch_network_unit('21-macvtap.netdev')
1969 networkctl_reload()
1970 self.wait_online('macvtap99:degraded',
1971 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1972
1973 @expectedFailureIfModuleIsNotAvailable('macvlan')
1974 def test_macvlan(self):
1975 first = True
1976 for mode in ['private', 'vepa', 'bridge', 'passthru']:
1977 if first:
1978 first = False
1979 else:
1980 self.tearDown()
1981
1982 print(f'### test_macvlan(mode={mode})')
1983 with self.subTest(mode=mode):
1984 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1985 '11-dummy.netdev', '25-macvlan.network')
1986 with open(os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8') as f:
1987 f.write('[MACVLAN]\nMode=' + mode)
1988 start_networkd()
1989
1990 self.wait_online('macvlan99:degraded',
1991 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
1992 self.networkctl_check_unit('macvlan99', '21-macvlan', '26-netdev-link-local-addressing-yes')
1993 self.networkctl_check_unit('test1', '11-dummy', '25-macvlan')
1994
1995 output = check_output('ip -d link show test1')
1996 print(output)
1997 self.assertIn(' mtu 2000 ', output)
1998
1999 output = check_output('ip -d link show macvlan99')
2000 print(output)
2001 self.assertIn(' mtu 2000 ', output)
2002 self.assertIn(f' macvlan mode {mode} ', output)
2003
2004 remove_link('test1')
2005 time.sleep(1)
2006
2007 check_output("ip link add test1 type dummy")
2008 self.wait_online('macvlan99:degraded',
2009 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2010
2011 output = check_output('ip -d link show test1')
2012 print(output)
2013 self.assertIn(' mtu 2000 ', output)
2014
2015 output = check_output('ip -d link show macvlan99')
2016 print(output)
2017 self.assertIn(' mtu 2000 ', output)
2018 self.assertIn(f' macvlan mode {mode} ', output)
2019 self.assertIn(' bcqueuelen 1234 ', output)
2020 if ' bclim ' in output: # This is new in kernel and iproute2 v6.4
2021 self.assertIn(' bclim 2147483647 ', output)
2022
2023 touch_network_unit('21-macvlan.netdev')
2024 networkctl_reload()
2025 self.wait_online('macvlan99:degraded',
2026 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
2027
2028 @expectedFailureIfModuleIsNotAvailable('ipvlan')
2029 def test_ipvlan(self):
2030 first = True
2031 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
2032 if first:
2033 first = False
2034 else:
2035 self.tearDown()
2036
2037 print(f'### test_ipvlan(mode={mode}, flag={flag})')
2038 with self.subTest(mode=mode, flag=flag):
2039 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
2040 '11-dummy.netdev', '25-ipvlan.network')
2041 with open(os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8') as f:
2042 f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
2043
2044 start_networkd()
2045 self.wait_online('ipvlan99:degraded', 'test1:degraded')
2046 self.networkctl_check_unit('ipvlan99', '25-ipvlan', '26-netdev-link-local-addressing-yes')
2047 self.networkctl_check_unit('test1', '11-dummy', '25-ipvlan')
2048
2049 output = check_output('ip -d link show ipvlan99')
2050 print(output)
2051 self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
2052
2053 touch_network_unit('25-ipvlan.netdev')
2054 networkctl_reload()
2055 self.wait_online('ipvlan99:degraded', 'test1:degraded')
2056
2057 @expectedFailureIfModuleIsNotAvailable('hsr')
2058 def test_hsr(self):
2059 first = True
2060 for proto, supervision in [['hsr', 9], ['prp', 127]]:
2061 if first:
2062 first = False
2063 else:
2064 self.tearDown()
2065
2066 print(f'### test_hsr(proto={proto}, supervision={supervision})')
2067 with self.subTest(proto=proto, supervision=supervision):
2068 copy_network_unit('25-hsr.netdev', '25-hsr.network',
2069 '11-dummy.netdev', '11-dummy.network',
2070 '12-dummy.netdev', '12-dummy-no-address.network')
2071 with open(os.path.join(network_unit_dir, '25-hsr.netdev'), mode='a', encoding='utf-8') as f:
2072 f.write('Protocol=' + proto + '\nSupervision=' + str(supervision))
2073
2074 start_networkd()
2075 self.wait_online('hsr99:degraded')
2076 self.networkctl_check_unit('hsr99', '25-hsr', '25-hsr')
2077 self.networkctl_check_unit('test1', '11-dummy', '11-dummy')
2078 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy-no-address')
2079
2080 output = check_output('ip -d link show hsr99')
2081 print(output)
2082 self.assertRegex(output, 'hsr slave1 test1 slave2 dummy98')
2083 self.assertRegex(output, f'supervision 01:15:4e:00:01:{supervision:02x}')
2084 self.assertRegex(output, 'proto ' + ('0' if proto == 'hsr' else '1') + ' ')
2085
2086 touch_network_unit('25-hsr.netdev')
2087 networkctl_reload()
2088 self.wait_online('hsr99:degraded')
2089
2090 @expectedFailureIfModuleIsNotAvailable('ipvtap')
2091 def test_ipvtap(self):
2092 first = True
2093 for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
2094 if first:
2095 first = False
2096 else:
2097 self.tearDown()
2098
2099 print(f'### test_ipvtap(mode={mode}, flag={flag})')
2100 with self.subTest(mode=mode, flag=flag):
2101 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
2102 '11-dummy.netdev', '25-ipvtap.network')
2103 with open(os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8') as f:
2104 f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
2105
2106 start_networkd()
2107 self.wait_online('ipvtap99:degraded', 'test1:degraded')
2108 self.networkctl_check_unit('ipvtap99', '25-ipvtap', '26-netdev-link-local-addressing-yes')
2109 self.networkctl_check_unit('test1', '11-dummy', '25-ipvtap')
2110
2111 output = check_output('ip -d link show ipvtap99')
2112 print(output)
2113 self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
2114
2115 touch_network_unit('25-ipvtap.netdev')
2116 networkctl_reload()
2117 self.wait_online('ipvtap99:degraded', 'test1:degraded')
2118
2119 def test_veth(self):
2120 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
2121 '25-veth-mtu.netdev')
2122 start_networkd()
2123
2124 self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
2125 self.networkctl_check_unit('veth99', '25-veth', '26-netdev-link-local-addressing-yes')
2126 self.networkctl_check_unit('veth-peer', '25-veth', '26-netdev-link-local-addressing-yes')
2127 self.networkctl_check_unit('veth-mtu', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
2128 self.networkctl_check_unit('veth-mtu-peer', '25-veth-mtu', '26-netdev-link-local-addressing-yes')
2129
2130 output = check_output('ip -d link show veth99')
2131 print(output)
2132 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
2133 output = check_output('ip -d link show veth-peer')
2134 print(output)
2135 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
2136
2137 output = check_output('ip -d link show veth-mtu')
2138 print(output)
2139 self.assertRegex(output, 'link/ether 12:34:56:78:9a:be')
2140 self.assertRegex(output, 'mtu 1800')
2141 output = check_output('ip -d link show veth-mtu-peer')
2142 print(output)
2143 self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
2144 self.assertRegex(output, 'mtu 1800')
2145
2146 touch_network_unit(
2147 '25-veth.netdev',
2148 '26-netdev-link-local-addressing-yes.network',
2149 '25-veth-mtu.netdev')
2150 networkctl_reload()
2151 self.wait_online(
2152 'veth99:degraded',
2153 'veth-peer:degraded',
2154 'veth-mtu:degraded',
2155 'veth-mtu-peer:degraded')
2156
2157 def check_tuntap(self, attached):
2158 pid = networkd_pid()
2159 name = psutil.Process(pid).name()[:15]
2160
2161 output = check_output('ip -d -oneline tuntap show')
2162 print(output)
2163 self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
2164 self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
2165
2166 if attached:
2167 self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)')
2168 self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)')
2169 self.assertRegex(output, r'testtap99: .*systemd\(1\)')
2170 self.assertRegex(output, r'testtun99: .*systemd\(1\)')
2171
2172 output = check_output('ip -d link show testtun99')
2173 print(output)
2174 # Old ip command does not support IFF_ flags
2175 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2176 self.assertIn('UP,LOWER_UP', output)
2177
2178 output = check_output('ip -d link show testtap99')
2179 print(output)
2180 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2181 self.assertIn('UP,LOWER_UP', output)
2182
2183 else:
2184 self.assertNotIn(f'{name}({pid})', output)
2185 self.assertNotIn('systemd(1)', output)
2186
2187 for _ in range(20):
2188 output = check_output('ip -d link show testtun99')
2189 print(output)
2190 self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
2191 if 'NO-CARRIER' in output:
2192 break
2193 time.sleep(0.5)
2194 else:
2195 self.fail()
2196
2197 for _ in range(20):
2198 output = check_output('ip -d link show testtap99')
2199 print(output)
2200 self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
2201 if 'NO-CARRIER' in output:
2202 break
2203 time.sleep(0.5)
2204 else:
2205 self.fail()
2206
2207 def test_tuntap(self):
2208 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2209 start_networkd()
2210 self.wait_online('testtun99:degraded', 'testtap99:degraded')
2211 self.networkctl_check_unit('testtap99', '25-tap', '26-netdev-link-local-addressing-yes')
2212 self.networkctl_check_unit('testtun99', '25-tun', '26-netdev-link-local-addressing-yes')
2213
2214 self.check_tuntap(True)
2215
2216 touch_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
2217 networkctl_reload()
2218 self.wait_online('testtun99:degraded', 'testtap99:degraded')
2219
2220 self.check_tuntap(True)
2221
2222 remove_network_unit('26-netdev-link-local-addressing-yes.network')
2223 restart_networkd()
2224 self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged')
2225 self.networkctl_check_unit('testtap99', '25-tap')
2226 self.networkctl_check_unit('testtun99', '25-tun')
2227
2228 self.check_tuntap(True)
2229
2230 clear_network_units()
2231 unmanage_existing_links()
2232 restart_networkd()
2233 self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged')
2234 self.networkctl_check_unit('testtap99')
2235 self.networkctl_check_unit('testtun99')
2236
2237 self.check_tuntap(False)
2238
2239 @expectedFailureIfModuleIsNotAvailable('vrf')
2240 def test_vrf(self):
2241 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2242 start_networkd()
2243
2244 self.wait_online('vrf99:carrier')
2245 self.networkctl_check_unit('vrf99', '25-vrf', '26-netdev-link-local-addressing-yes')
2246
2247 touch_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
2248 networkctl()
2249 self.wait_online('vrf99:carrier')
2250
2251 @expectedFailureIfModuleIsNotAvailable('vcan')
2252 def test_vcan(self):
2253 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
2254 '25-vcan98.netdev', '25-vcan98.network')
2255 start_networkd()
2256
2257 self.wait_online('vcan99:carrier', 'vcan98:carrier')
2258 # For can devices, 'carrier' is the default required operational state.
2259 self.wait_online('vcan99', 'vcan98')
2260 self.networkctl_check_unit('vcan99', '25-vcan', '26-netdev-link-local-addressing-yes')
2261 self.networkctl_check_unit('vcan98', '25-vcan98', '25-vcan98')
2262
2263 # https://github.com/systemd/systemd/issues/30140
2264 output = check_output('ip -d link show vcan99')
2265 print(output)
2266 self.assertIn('mtu 16 ', output)
2267
2268 output = check_output('ip -d link show vcan98')
2269 print(output)
2270 self.assertIn('mtu 16 ', output)
2271
2272 touch_network_unit(
2273 '25-vcan.netdev',
2274 '26-netdev-link-local-addressing-yes.network',
2275 '25-vcan98.netdev',
2276 '25-vcan98.network')
2277 networkctl_reload()
2278 self.wait_online('vcan99:carrier', 'vcan98:carrier')
2279
2280 @expectedFailureIfModuleIsNotAvailable('vxcan')
2281 def test_vxcan(self):
2282 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2283 start_networkd()
2284
2285 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
2286 # For can devices, 'carrier' is the default required operational state.
2287 self.wait_online('vxcan99', 'vxcan-peer')
2288 self.networkctl_check_unit('vxcan99', '25-vxcan', '26-netdev-link-local-addressing-yes')
2289 self.networkctl_check_unit('vxcan-peer', '25-vxcan', '26-netdev-link-local-addressing-yes')
2290
2291 touch_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
2292 networkctl()
2293 self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
2294
2295 @expectedFailureIfModuleIsNotAvailable('wireguard')
2296 def test_wireguard(self):
2297 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
2298 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
2299 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
2300
2301 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
2302 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
2303 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
2304 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2305 start_networkd()
2306 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
2307 self.networkctl_check_unit('wg99', '25-wireguard', '25-wireguard')
2308 self.networkctl_check_unit('wg98', '25-wireguard-23-peers', '25-wireguard-23-peers')
2309 self.networkctl_check_unit('wg97', '25-wireguard-no-peer', '25-wireguard-no-peer')
2310
2311 output = check_output('ip -4 address show dev wg99')
2312 print(output)
2313 self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
2314
2315 output = check_output('ip -4 address show dev wg99')
2316 print(output)
2317 self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
2318
2319 output = check_output('ip -6 address show dev wg99')
2320 print(output)
2321 self.assertIn('inet6 fe80::1/64 scope link', output)
2322
2323 output = check_output('ip -4 address show dev wg98')
2324 print(output)
2325 self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
2326
2327 output = check_output('ip -6 address show dev wg98')
2328 print(output)
2329 self.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output)
2330
2331 output = check_output('ip -4 route show dev wg99 table 1234')
2332 print(output)
2333 self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
2334
2335 output = check_output('ip -6 route show dev wg99 table 1234')
2336 print(output)
2337 self.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output)
2338
2339 output = check_output('ip -6 route show dev wg98 table 1234')
2340 print(output)
2341 self.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output)
2342 self.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output)
2343 self.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output)
2344 self.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output)
2345 self.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output)
2346 self.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output)
2347 self.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output)
2348 self.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output)
2349 self.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output)
2350 self.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output)
2351 self.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output)
2352 self.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output)
2353 self.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output)
2354 self.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output)
2355 self.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output)
2356 self.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output)
2357 self.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output)
2358 self.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output)
2359 self.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output)
2360 self.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output)
2361 self.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output)
2362 self.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output)
2363 self.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output)
2364 self.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output)
2365 self.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output)
2366 self.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output)
2367 self.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output)
2368 self.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output)
2369 self.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output)
2370 self.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output)
2371 self.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output)
2372 self.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output)
2373 self.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output)
2374 self.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output)
2375 self.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output)
2376 self.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output)
2377 self.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output)
2378 self.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output)
2379 self.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output)
2380 self.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output)
2381 self.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output)
2382 self.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output)
2383 self.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output)
2384 self.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output)
2385 self.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output)
2386 self.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output)
2387
2388 if shutil.which('wg'):
2389 call('wg')
2390
2391 output = check_output('wg show wg99 listen-port')
2392 self.assertEqual(output, '51820')
2393 output = check_output('wg show wg99 fwmark')
2394 self.assertEqual(output, '0x4d2')
2395 output = check_output('wg show wg99 private-key')
2396 self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
2397 output = check_output('wg show wg99 allowed-ips')
2398 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
2399 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
2400 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
2401 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
2402 output = check_output('wg show wg99 persistent-keepalive')
2403 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
2404 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
2405 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
2406 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
2407 output = check_output('wg show wg99 endpoints')
2408 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
2409 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
2410 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
2411 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
2412 output = check_output('wg show wg99 preshared-keys')
2413 self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
2414 self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
2415 self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
2416 self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
2417
2418 output = check_output('wg show wg98 private-key')
2419 self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
2420
2421 output = check_output('wg show wg97 listen-port')
2422 self.assertEqual(output, '51821')
2423 output = check_output('wg show wg97 fwmark')
2424 self.assertEqual(output, '0x4d3')
2425
2426 touch_network_unit(
2427 '25-wireguard.netdev', '25-wireguard.network',
2428 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
2429 '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
2430 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
2431 networkctl_reload()
2432 self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
2433
2434 def test_geneve(self):
2435 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2436 start_networkd()
2437
2438 self.wait_online('geneve99:degraded')
2439 self.networkctl_check_unit('geneve99', '25-geneve', '26-netdev-link-local-addressing-yes')
2440
2441 output = check_output('ip -d link show geneve99')
2442 print(output)
2443 self.assertRegex(output, '192.168.22.1')
2444 self.assertRegex(output, '6082')
2445 self.assertRegex(output, 'udpcsum')
2446 self.assertRegex(output, 'udp6zerocsumrx')
2447
2448 touch_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
2449 networkctl_reload()
2450 self.wait_online('geneve99:degraded')
2451
2452 def _test_ipip_tunnel(self, mode):
2453 copy_network_unit('12-dummy.netdev', '25-ipip.network',
2454 '25-ipip-tunnel.netdev', '25-tunnel.network',
2455 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2456 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2457 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2458
2459 if mode:
2460 for netdev in ['25-ipip-tunnel.netdev',
2461 '25-ipip-tunnel-local-any.netdev',
2462 '25-ipip-tunnel-remote-any.netdev',
2463 '25-ipip-tunnel-any-any.netdev']:
2464 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2465 f.write(f'[Tunnel]\nMode={mode}\n')
2466 else:
2467 mode = 'ipip' # kernel default
2468
2469 start_networkd()
2470 self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
2471
2472 output = check_output('ip -d link show ipiptun99')
2473 print(output)
2474 self.assertIn(f'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98', output)
2475 output = check_output('ip -d link show ipiptun98')
2476 print(output)
2477 self.assertIn(f'ipip {mode} remote 192.169.224.239 local any dev dummy98', output)
2478 output = check_output('ip -d link show ipiptun97')
2479 print(output)
2480 self.assertIn(f'ipip {mode} remote any local 192.168.223.238 dev dummy98', output)
2481 output = check_output('ip -d link show ipiptun96')
2482 print(output)
2483 self.assertIn(f'ipip {mode} remote any local any dev dummy98', output)
2484
2485 touch_network_unit(
2486 '25-ipip-tunnel.netdev',
2487 '25-ipip-tunnel-local-any.netdev',
2488 '25-ipip-tunnel-remote-any.netdev',
2489 '25-ipip-tunnel-any-any.netdev')
2490 networkctl_reload()
2491 self.wait_online(
2492 'ipiptun99:routable',
2493 'ipiptun98:routable',
2494 'ipiptun97:routable',
2495 'ipiptun96:routable',
2496 'dummy98:degraded')
2497
2498 def test_ipip_tunnel(self):
2499 first = True
2500 for mode in [None, 'ipip', 'any']:
2501 if first:
2502 first = False
2503 else:
2504 self.tearDown()
2505
2506 print(f'### test_ipip_tunnel(mode={mode})')
2507 with self.subTest(mode=mode):
2508 self._test_ipip_tunnel(mode)
2509
2510 def test_gre_tunnel(self):
2511 copy_network_unit('12-dummy.netdev', '25-gretun.network',
2512 '25-gre-tunnel.netdev', '25-tunnel.network',
2513 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2514 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2515 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2516 start_networkd()
2517 self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2518 self.networkctl_check_unit('gretun99', '25-gre-tunnel', '25-tunnel')
2519 self.networkctl_check_unit('gretun98', '25-gre-tunnel-local-any', '25-tunnel-local-any')
2520 self.networkctl_check_unit('gretun97', '25-gre-tunnel-remote-any', '25-tunnel-remote-any')
2521 self.networkctl_check_unit('gretun96', '25-gre-tunnel-any-any', '25-tunnel-any-any')
2522 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretun')
2523
2524 output = check_output('ip -d link show gretun99')
2525 print(output)
2526 self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2527 self.assertRegex(output, 'ikey 1.2.3.103')
2528 self.assertRegex(output, 'okey 1.2.4.103')
2529 self.assertRegex(output, 'iseq')
2530 self.assertRegex(output, 'oseq')
2531 output = check_output('ip -d link show gretun98')
2532 print(output)
2533 self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
2534 self.assertRegex(output, 'ikey 0.0.0.104')
2535 self.assertRegex(output, 'okey 0.0.0.104')
2536 self.assertNotRegex(output, 'iseq')
2537 self.assertNotRegex(output, 'oseq')
2538 output = check_output('ip -d link show gretun97')
2539 print(output)
2540 self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
2541 self.assertRegex(output, 'ikey 0.0.0.105')
2542 self.assertRegex(output, 'okey 0.0.0.105')
2543 self.assertNotRegex(output, 'iseq')
2544 self.assertNotRegex(output, 'oseq')
2545 output = check_output('ip -d link show gretun96')
2546 print(output)
2547 self.assertRegex(output, 'gre remote any local any dev dummy98')
2548 self.assertRegex(output, 'ikey 0.0.0.106')
2549 self.assertRegex(output, 'okey 0.0.0.106')
2550 self.assertNotRegex(output, 'iseq')
2551 self.assertNotRegex(output, 'oseq')
2552
2553 touch_network_unit(
2554 '25-gre-tunnel.netdev',
2555 '25-gre-tunnel-local-any.netdev',
2556 '25-gre-tunnel-remote-any.netdev',
2557 '25-gre-tunnel-any-any.netdev')
2558 networkctl_reload()
2559 self.wait_online(
2560 'gretun99:routable',
2561 'gretun98:routable',
2562 'gretun97:routable',
2563 'gretun96:routable',
2564 'dummy98:degraded')
2565
2566 def test_ip6gre_tunnel(self):
2567 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2568 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2569 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2570 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2571 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2572 start_networkd()
2573
2574 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2575
2576 self.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2577
2578 output = check_output('ip -d link show ip6gretun99')
2579 print(output)
2580 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2581 output = check_output('ip -d link show ip6gretun98')
2582 print(output)
2583 self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2584 output = check_output('ip -d link show ip6gretun97')
2585 print(output)
2586 self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2587 output = check_output('ip -d link show ip6gretun96')
2588 print(output)
2589 self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
2590
2591 touch_network_unit(
2592 '25-ip6gre-tunnel.netdev',
2593 '25-ip6gre-tunnel-local-any.netdev',
2594 '25-ip6gre-tunnel-remote-any.netdev',
2595 '25-ip6gre-tunnel-any-any.netdev')
2596 networkctl_reload()
2597 self.wait_links(
2598 'dummy98',
2599 'ip6gretun99',
2600 'ip6gretun98',
2601 'ip6gretun97',
2602 'ip6gretun96')
2603
2604 def test_gretap_tunnel(self):
2605 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2606 '25-gretap-tunnel.netdev', '25-tunnel.network',
2607 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2608 start_networkd()
2609 self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2610 self.networkctl_check_unit('gretap99', '25-gretap-tunnel', '25-tunnel')
2611 self.networkctl_check_unit('gretap98', '25-gretap-tunnel-local-any', '25-tunnel-local-any')
2612 self.networkctl_check_unit('dummy98', '12-dummy', '25-gretap')
2613
2614 output = check_output('ip -d link show gretap99')
2615 print(output)
2616 self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2617 self.assertRegex(output, 'ikey 0.0.0.106')
2618 self.assertRegex(output, 'okey 0.0.0.106')
2619 self.assertRegex(output, 'iseq')
2620 self.assertRegex(output, 'oseq')
2621 self.assertIn('nopmtudisc', output)
2622 self.assertIn('ignore-df', output)
2623 output = check_output('ip -d link show gretap98')
2624 print(output)
2625 self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
2626 self.assertRegex(output, 'ikey 0.0.0.107')
2627 self.assertRegex(output, 'okey 0.0.0.107')
2628 self.assertRegex(output, 'iseq')
2629 self.assertRegex(output, 'oseq')
2630
2631 touch_network_unit(
2632 '25-gretap-tunnel.netdev',
2633 '25-gretap-tunnel-local-any.netdev')
2634 networkctl_reload()
2635 self.wait_online(
2636 'gretap99:routable',
2637 'gretap98:routable',
2638 'dummy98:degraded')
2639
2640 def test_ip6gretap_tunnel(self):
2641 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2642 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2643 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2644 start_networkd()
2645 self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2646 self.networkctl_check_unit('ip6gretap99', '25-ip6gretap-tunnel', '25-tunnel')
2647 self.networkctl_check_unit('ip6gretap98', '25-ip6gretap-tunnel-local-any', '25-tunnel-local-any')
2648 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6gretap')
2649
2650 output = check_output('ip -d link show ip6gretap99')
2651 print(output)
2652 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2653 output = check_output('ip -d link show ip6gretap98')
2654 print(output)
2655 self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2656
2657 touch_network_unit(
2658 '25-ip6gretap-tunnel.netdev',
2659 '25-ip6gretap-tunnel-local-any.netdev')
2660 networkctl_reload()
2661 self.wait_online(
2662 'ip6gretap99:routable',
2663 'ip6gretap98:routable',
2664 'dummy98:degraded')
2665
2666 def test_vti_tunnel(self):
2667 copy_network_unit('12-dummy.netdev', '25-vti.network',
2668 '25-vti-tunnel.netdev', '25-tunnel.network',
2669 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2670 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2671 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2672 start_networkd()
2673 self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2674 self.networkctl_check_unit('vtitun99', '25-vti-tunnel', '25-tunnel')
2675 self.networkctl_check_unit('vtitun98', '25-vti-tunnel-local-any', '25-tunnel-local-any')
2676 self.networkctl_check_unit('vtitun97', '25-vti-tunnel-remote-any', '25-tunnel-remote-any')
2677 self.networkctl_check_unit('vtitun96', '25-vti-tunnel-any-any', '25-tunnel-any-any')
2678 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti')
2679
2680 output = check_output('ip -d link show vtitun99')
2681 print(output)
2682 self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2683 output = check_output('ip -d link show vtitun98')
2684 print(output)
2685 self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
2686 output = check_output('ip -d link show vtitun97')
2687 print(output)
2688 self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
2689 output = check_output('ip -d link show vtitun96')
2690 print(output)
2691 self.assertRegex(output, 'vti remote any local any dev dummy98')
2692
2693 touch_network_unit(
2694 '25-vti-tunnel.netdev',
2695 '25-vti-tunnel-local-any.netdev',
2696 '25-vti-tunnel-remote-any.netdev',
2697 '25-vti-tunnel-any-any.netdev')
2698 networkctl_reload()
2699 self.wait_online(
2700 'vtitun99:routable',
2701 'vtitun98:routable',
2702 'vtitun97:routable',
2703 'vtitun96:routable',
2704 'dummy98:degraded')
2705
2706 def test_vti6_tunnel(self):
2707 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2708 '25-vti6-tunnel.netdev', '25-tunnel.network',
2709 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2710 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2711 start_networkd()
2712 self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2713 self.networkctl_check_unit('vti6tun99', '25-vti6-tunnel', '25-tunnel')
2714 self.networkctl_check_unit('vti6tun98', '25-vti6-tunnel-local-any', '25-tunnel-local-any')
2715 self.networkctl_check_unit('vti6tun97', '25-vti6-tunnel-remote-any', '25-tunnel-remote-any')
2716 self.networkctl_check_unit('dummy98', '12-dummy', '25-vti6')
2717
2718 output = check_output('ip -d link show vti6tun99')
2719 print(output)
2720 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2721 output = check_output('ip -d link show vti6tun98')
2722 print(output)
2723 self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2724 output = check_output('ip -d link show vti6tun97')
2725 print(output)
2726 self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2727
2728 touch_network_unit(
2729 '25-vti6-tunnel.netdev',
2730 '25-vti6-tunnel-local-any.netdev',
2731 '25-vti6-tunnel-remote-any.netdev')
2732 networkctl_reload()
2733 self.wait_online(
2734 'vti6tun99:routable',
2735 'vti6tun98:routable',
2736 'vti6tun97:routable',
2737 'dummy98:degraded')
2738
2739 def _test_ip6tnl_tunnel(self, mode):
2740 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2741 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2742 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2743 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2744 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2745 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2746 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2747
2748 if mode:
2749 for netdev in ['25-ip6tnl-tunnel.netdev',
2750 '25-ip6tnl-tunnel-local-any.netdev',
2751 '25-ip6tnl-tunnel-remote-any.netdev',
2752 '25-ip6tnl-tunnel-local-slaac.netdev',
2753 '25-ip6tnl-tunnel-external.netdev']:
2754 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2755 f.write(f'[Tunnel]\nMode={mode}\n')
2756 else:
2757 mode = 'any' # kernel default
2758
2759 start_networkd()
2760 self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2761 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2762 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2763 self.networkctl_check_unit('ip6tnl99', '25-ip6tnl-tunnel', '25-tunnel')
2764 self.networkctl_check_unit('ip6tnl98', '25-ip6tnl-tunnel-local-any', '25-tunnel-local-any')
2765 self.networkctl_check_unit('ip6tnl97', '25-ip6tnl-tunnel-remote-any', '25-tunnel-remote-any')
2766 self.networkctl_check_unit('ip6tnl-slaac', '25-ip6tnl-tunnel-local-slaac', '25-ip6tnl-tunnel-local-slaac')
2767 self.networkctl_check_unit('ip6tnl-external', '25-ip6tnl-tunnel-external', '26-netdev-link-local-addressing-yes')
2768 self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6tnl')
2769 self.networkctl_check_unit('veth99', '25-veth', '25-ip6tnl-slaac')
2770 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
2771
2772 output = check_output('ip -d link show ip6tnl99')
2773 print(output)
2774 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
2775 output = check_output('ip -d link show ip6tnl98')
2776 print(output)
2777 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98', output)
2778 output = check_output('ip -d link show ip6tnl97')
2779 print(output)
2780 self.assertIn(f'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98', output)
2781 output = check_output('ip -d link show ip6tnl-external')
2782 print(output)
2783 self.assertIn('ip6tnl-external@NONE:', output)
2784 self.assertIn('ip6tnl external ', output)
2785 output = check_output('ip -d link show ip6tnl-slaac')
2786 print(output)
2787 self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
2788
2789 output = check_output('ip -6 address show veth99')
2790 print(output)
2791 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
2792
2793 output = check_output('ip -4 route show default')
2794 print(output)
2795 self.assertIn('default dev ip6tnl-slaac proto static', output)
2796
2797 touch_network_unit(
2798 '25-ip6tnl-tunnel.netdev',
2799 '25-ip6tnl-tunnel-local-any.netdev',
2800 '25-ip6tnl-tunnel-remote-any.netdev',
2801 '25-ip6tnl-tunnel-local-slaac.netdev',
2802 '25-ip6tnl-tunnel-external.netdev')
2803 networkctl_reload()
2804 self.wait_online(
2805 'ip6tnl99:routable',
2806 'ip6tnl98:routable',
2807 'ip6tnl97:routable',
2808 'ip6tnl-slaac:degraded',
2809 'ip6tnl-external:degraded',
2810 'dummy98:degraded',
2811 'veth99:routable',
2812 'veth-peer:degraded')
2813
2814 def test_ip6tnl_tunnel(self):
2815 first = True
2816 for mode in [None, 'ipip6', 'ip6ip6', 'any']:
2817 if first:
2818 first = False
2819 else:
2820 self.tearDown()
2821
2822 print(f'### test_ip6tnl_tunnel(mode={mode})')
2823 with self.subTest(mode=mode):
2824 self._test_ip6tnl_tunnel(mode)
2825
2826 def _test_sit_tunnel(self, mode):
2827 copy_network_unit('12-dummy.netdev', '25-sit.network',
2828 '25-sit-tunnel.netdev', '25-tunnel.network',
2829 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2830 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2831 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2832
2833 if mode:
2834 for netdev in ['25-sit-tunnel.netdev',
2835 '25-sit-tunnel-local-any.netdev',
2836 '25-sit-tunnel-remote-any.netdev',
2837 '25-sit-tunnel-any-any.netdev']:
2838 with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f:
2839 f.write(f'[Tunnel]\nMode={mode}\n')
2840 else:
2841 mode = 'ip6ip' # kernel default
2842
2843 start_networkd()
2844 self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2845 self.networkctl_check_unit('sittun99', '25-sit-tunnel', '25-tunnel')
2846 self.networkctl_check_unit('sittun98', '25-sit-tunnel-local-any', '25-tunnel-local-any')
2847 self.networkctl_check_unit('sittun97', '25-sit-tunnel-remote-any', '25-tunnel-remote-any')
2848 self.networkctl_check_unit('sittun96', '25-sit-tunnel-any-any', '25-tunnel-any-any')
2849 self.networkctl_check_unit('dummy98', '12-dummy', '25-sit')
2850
2851 output = check_output('ip -d link show sittun99')
2852 print(output)
2853 self.assertIn(f'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98', output)
2854 output = check_output('ip -d link show sittun98')
2855 print(output)
2856 self.assertIn(f'sit {mode} remote 10.65.223.239 local any dev dummy98', output)
2857 output = check_output('ip -d link show sittun97')
2858 print(output)
2859 self.assertIn(f'sit {mode} remote any local 10.65.223.238 dev dummy98', output)
2860 output = check_output('ip -d link show sittun96')
2861 print(output)
2862 self.assertIn(f'sit {mode} remote any local any dev dummy98', output)
2863
2864 touch_network_unit(
2865 '25-sit-tunnel.netdev',
2866 '25-sit-tunnel-local-any.netdev',
2867 '25-sit-tunnel-remote-any.netdev',
2868 '25-sit-tunnel-any-any.netdev')
2869 networkctl_reload()
2870 self.wait_online(
2871 'sittun99:routable',
2872 'sittun98:routable',
2873 'sittun97:routable',
2874 'sittun96:routable',
2875 'dummy98:degraded')
2876
2877 def test_sit_tunnel(self):
2878 first = True
2879 for mode in [None, 'ipip', 'ip6ip', 'any']:
2880 if first:
2881 first = False
2882 else:
2883 self.tearDown()
2884
2885 print(f'### test_sit_tunnel(mode={mode})')
2886 with self.subTest(mode=mode):
2887 self._test_sit_tunnel(mode)
2888
2889 def test_isatap_tunnel(self):
2890 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2891 '25-isatap-tunnel.netdev', '25-tunnel.network')
2892 start_networkd()
2893 self.wait_online('isataptun99:routable', 'dummy98:degraded')
2894 self.networkctl_check_unit('isataptun99', '25-isatap-tunnel', '25-tunnel')
2895 self.networkctl_check_unit('dummy98', '12-dummy', '25-isatap')
2896
2897 output = check_output('ip -d link show isataptun99')
2898 print(output)
2899 self.assertRegex(output, "isatap ")
2900
2901 touch_network_unit('25-isatap-tunnel.netdev')
2902 networkctl_reload()
2903 self.wait_online('isataptun99:routable', 'dummy98:degraded')
2904
2905 def test_6rd_tunnel(self):
2906 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2907 '25-6rd-tunnel.netdev', '25-tunnel.network')
2908 start_networkd()
2909 self.wait_online('sittun99:routable', 'dummy98:degraded')
2910 self.networkctl_check_unit('sittun99', '25-6rd-tunnel', '25-tunnel')
2911 self.networkctl_check_unit('dummy98', '12-dummy', '25-6rd')
2912
2913 output = check_output('ip -d link show sittun99')
2914 print(output)
2915 self.assertRegex(output, '6rd-prefix 2602::/24')
2916
2917 touch_network_unit('25-6rd-tunnel.netdev')
2918 networkctl_reload()
2919 self.wait_online('sittun99:routable', 'dummy98:degraded')
2920
2921 @expectedFailureIfERSPANv0IsNotSupported()
2922 def test_erspan_tunnel_v0(self):
2923 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2924 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2925 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2926 start_networkd()
2927 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2928 self.networkctl_check_unit('erspan99', '25-erspan0-tunnel', '25-tunnel')
2929 self.networkctl_check_unit('erspan98', '25-erspan0-tunnel-local-any', '25-tunnel-local-any')
2930 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2931
2932 output = check_output('ip -d link show erspan99')
2933 print(output)
2934 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2935 self.assertIn('erspan_ver 0', output)
2936 self.assertNotIn('erspan_index 123', output)
2937 self.assertNotIn('erspan_dir ingress', output)
2938 self.assertNotIn('erspan_hwid 1f', output)
2939 self.assertIn('ikey 0.0.0.101', output)
2940 self.assertIn('iseq', output)
2941 self.assertIn('nopmtudisc', output)
2942 self.assertIn('ignore-df', output)
2943 output = check_output('ip -d link show erspan98')
2944 print(output)
2945 self.assertIn('erspan remote 172.16.1.100 local any', output)
2946 self.assertIn('erspan_ver 0', output)
2947 self.assertNotIn('erspan_index 124', output)
2948 self.assertNotIn('erspan_dir egress', output)
2949 self.assertNotIn('erspan_hwid 2f', output)
2950 self.assertIn('ikey 0.0.0.102', output)
2951 self.assertIn('iseq', output)
2952
2953 touch_network_unit(
2954 '25-erspan0-tunnel.netdev',
2955 '25-erspan0-tunnel-local-any.netdev')
2956 networkctl_reload()
2957 self.wait_online(
2958 'erspan99:routable',
2959 'erspan98:routable',
2960 'dummy98:degraded')
2961
2962 def test_erspan_tunnel_v1(self):
2963 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2964 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2965 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2966 start_networkd()
2967 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2968 self.networkctl_check_unit('erspan99', '25-erspan1-tunnel', '25-tunnel')
2969 self.networkctl_check_unit('erspan98', '25-erspan1-tunnel-local-any', '25-tunnel-local-any')
2970 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
2971
2972 output = check_output('ip -d link show erspan99')
2973 print(output)
2974 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
2975 self.assertIn('erspan_ver 1', output)
2976 self.assertIn('erspan_index 123', output)
2977 self.assertNotIn('erspan_dir ingress', output)
2978 self.assertNotIn('erspan_hwid 1f', output)
2979 self.assertIn('ikey 0.0.0.101', output)
2980 self.assertIn('okey 0.0.0.101', output)
2981 self.assertIn('iseq', output)
2982 self.assertIn('oseq', output)
2983 output = check_output('ip -d link show erspan98')
2984 print(output)
2985 self.assertIn('erspan remote 172.16.1.100 local any', output)
2986 self.assertIn('erspan_ver 1', output)
2987 self.assertIn('erspan_index 124', output)
2988 self.assertNotIn('erspan_dir egress', output)
2989 self.assertNotIn('erspan_hwid 2f', output)
2990 self.assertIn('ikey 0.0.0.102', output)
2991 self.assertIn('okey 0.0.0.102', output)
2992 self.assertIn('iseq', output)
2993 self.assertIn('oseq', output)
2994
2995 touch_network_unit(
2996 '25-erspan1-tunnel.netdev',
2997 '25-erspan1-tunnel-local-any.netdev')
2998 networkctl_reload()
2999 self.wait_online(
3000 'erspan99:routable',
3001 'erspan98:routable',
3002 'dummy98:degraded')
3003
3004 @expectedFailureIfERSPANv2IsNotSupported()
3005 def test_erspan_tunnel_v2(self):
3006 copy_network_unit('12-dummy.netdev', '25-erspan.network',
3007 '25-erspan2-tunnel.netdev', '25-tunnel.network',
3008 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
3009 start_networkd()
3010 self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
3011 self.networkctl_check_unit('erspan99', '25-erspan2-tunnel', '25-tunnel')
3012 self.networkctl_check_unit('erspan98', '25-erspan2-tunnel-local-any', '25-tunnel-local-any')
3013 self.networkctl_check_unit('dummy98', '12-dummy', '25-erspan')
3014
3015 output = check_output('ip -d link show erspan99')
3016 print(output)
3017 self.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output)
3018 self.assertIn('erspan_ver 2', output)
3019 self.assertNotIn('erspan_index 123', output)
3020 self.assertIn('erspan_dir ingress', output)
3021 self.assertIn('erspan_hwid 0x1f', output)
3022 self.assertIn('ikey 0.0.0.101', output)
3023 self.assertIn('okey 0.0.0.101', output)
3024 self.assertIn('iseq', output)
3025 self.assertIn('oseq', output)
3026 output = check_output('ip -d link show erspan98')
3027 print(output)
3028 self.assertIn('erspan remote 172.16.1.100 local any', output)
3029 self.assertIn('erspan_ver 2', output)
3030 self.assertNotIn('erspan_index 124', output)
3031 self.assertIn('erspan_dir egress', output)
3032 self.assertIn('erspan_hwid 0x2f', output)
3033 self.assertIn('ikey 0.0.0.102', output)
3034 self.assertIn('okey 0.0.0.102', output)
3035 self.assertIn('iseq', output)
3036 self.assertIn('oseq', output)
3037
3038 touch_network_unit(
3039 '25-erspan2-tunnel.netdev',
3040 '25-erspan2-tunnel-local-any.netdev')
3041 networkctl_reload()
3042 self.wait_online(
3043 'erspan99:routable',
3044 'erspan98:routable',
3045 'dummy98:degraded')
3046
3047 def test_tunnel_independent(self):
3048 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
3049 start_networkd()
3050
3051 self.wait_online('ipiptun99:carrier')
3052 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent', '26-netdev-link-local-addressing-yes')
3053
3054 def test_tunnel_independent_loopback(self):
3055 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
3056 start_networkd()
3057
3058 self.wait_online('ipiptun99:carrier')
3059 self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent-loopback', '26-netdev-link-local-addressing-yes')
3060
3061 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
3062 def test_xfrm(self):
3063 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
3064 '25-xfrm.netdev', '25-xfrm-independent.netdev',
3065 '26-netdev-link-local-addressing-yes.network')
3066 start_networkd()
3067
3068 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
3069 self.networkctl_check_unit('dummy98', '12-dummy', '25-xfrm')
3070 self.networkctl_check_unit('xfrm98', '25-xfrm', '26-netdev-link-local-addressing-yes')
3071 self.networkctl_check_unit('xfrm99', '25-xfrm-independent', '26-netdev-link-local-addressing-yes')
3072
3073 output = check_output('ip -d link show dev xfrm98')
3074 print(output)
3075 self.assertIn('xfrm98@dummy98:', output)
3076 self.assertIn('xfrm if_id 0x98 ', output)
3077
3078 output = check_output('ip -d link show dev xfrm99')
3079 print(output)
3080 self.assertIn('xfrm99@lo:', output)
3081 self.assertIn('xfrm if_id 0x99 ', output)
3082
3083 touch_network_unit('25-xfrm.netdev')
3084 networkctl_reload()
3085 self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
3086
3087 @expectedFailureIfModuleIsNotAvailable('fou')
3088 def test_fou(self):
3089 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3090 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3091 '25-fou-gre.netdev', '25-fou-gretap.netdev')
3092 start_networkd()
3093
3094 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
3095 self.networkctl_check_unit('ipiptun96', '25-fou-ipip')
3096 self.networkctl_check_unit('sittun96', '25-fou-sit')
3097 self.networkctl_check_unit('gretun96', '25-fou-gre')
3098 self.networkctl_check_unit('gretap96', '25-fou-gretap')
3099
3100 output = check_output('ip fou show')
3101 print(output)
3102 self.assertRegex(output, 'port 55555 ipproto 4')
3103 self.assertRegex(output, 'port 55556 ipproto 47')
3104
3105 output = check_output('ip -d link show ipiptun96')
3106 print(output)
3107 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
3108 output = check_output('ip -d link show sittun96')
3109 print(output)
3110 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
3111 output = check_output('ip -d link show gretun96')
3112 print(output)
3113 self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
3114 output = check_output('ip -d link show gretap96')
3115 print(output)
3116 self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
3117
3118 touch_network_unit(
3119 '25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
3120 '25-fou-ipip.netdev', '25-fou-sit.netdev',
3121 '25-fou-gre.netdev', '25-fou-gretap.netdev')
3122 networkctl_reload()
3123 self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
3124
3125 def test_vxlan(self):
3126 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
3127 '25-vxlan.netdev', '25-vxlan.network',
3128 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
3129 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
3130 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
3131 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network',
3132 '25-vxlan-external.netdev', '25-vxlan-external.network')
3133 start_networkd()
3134
3135 self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
3136 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded',
3137 'vxlan-external:degraded')
3138 self.networkctl_check_unit('test1', '11-dummy', '25-vxlan-test1')
3139 self.networkctl_check_unit('veth99', '25-veth', '25-vxlan-veth99')
3140 self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix')
3141 self.networkctl_check_unit('vxlan99', '25-vxlan', '25-vxlan')
3142 self.networkctl_check_unit('vxlan98', '25-vxlan-independent', '26-netdev-link-local-addressing-yes')
3143 self.networkctl_check_unit('vxlan97', '25-vxlan-ipv6', '25-vxlan-ipv6')
3144 self.networkctl_check_unit('vxlan-slaac', '25-vxlan-local-slaac', '25-vxlan-local-slaac')
3145 self.networkctl_check_unit('vxlan-external', '25-vxlan-external', '25-vxlan-external')
3146
3147 output = check_output('ip -d -d link show vxlan99')
3148 print(output)
3149 self.assertIn('999', output)
3150 self.assertIn('5555', output)
3151 self.assertIn('l2miss', output)
3152 self.assertIn('l3miss', output)
3153 self.assertIn('gbp', output)
3154 # Since [0] some of the options use slightly different names and some
3155 # options with default values are shown only if the -d(etails) setting
3156 # is repeated
3157 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
3158 self.assertRegex(output, '(udpcsum|udp_csum)')
3159 self.assertRegex(output, '(udp6zerocsumtx|udp_zero_csum6_tx)')
3160 self.assertRegex(output, '(udp6zerocsumrx|udp_zero_csum6_rx)')
3161 self.assertRegex(output, '(remcsumtx|remcsum_tx)')
3162 self.assertRegex(output, '(remcsumrx|remcsum_rx)')
3163
3164 output = check_output('bridge fdb show dev vxlan99')
3165 print(output)
3166 self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
3167 self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
3168 self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
3169
3170 output = networkctl_status('vxlan99')
3171 print(output)
3172 self.assertIn('VNI: 999', output)
3173 self.assertIn('Destination Port: 5555', output)
3174 self.assertIn('Underlying Device: test1', output)
3175
3176 output = check_output('bridge fdb show dev vxlan97')
3177 print(output)
3178 self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
3179 self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
3180 self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
3181
3182 output = check_output('ip -d link show vxlan-slaac')
3183 print(output)
3184 self.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
3185
3186 output = check_output('ip -6 address show veth99')
3187 print(output)
3188 self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
3189
3190 output = check_output('ip -d link show vxlan-external')
3191 print(output)
3192 self.assertIn('id 0 ', output)
3193 self.assertIn('external', output)
3194 self.assertIn('vnifilter', output)
3195
3196 @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
3197 def test_macsec(self):
3198 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
3199 '26-macsec.network', '12-dummy.netdev')
3200 start_networkd()
3201
3202 self.wait_online('dummy98:degraded', 'macsec99:routable')
3203 self.networkctl_check_unit('dummy98', '12-dummy', '26-macsec')
3204 self.networkctl_check_unit('macsec99', '25-macsec', '25-macsec')
3205
3206 output = check_output('ip -d link show macsec99')
3207 print(output)
3208 self.assertRegex(output, 'macsec99@dummy98')
3209 self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
3210 self.assertRegex(output, 'encrypt on')
3211
3212 output = check_output('ip macsec show macsec99')
3213 print(output)
3214 self.assertRegex(output, 'encrypt on')
3215 self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
3216 self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
3217 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
3218 self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
3219 self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
3220 self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
3221 self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
3222 self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
3223 self.assertNotRegex(output, 'key 02030405067080900000000000000000')
3224 self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
3225 self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
3226
3227 touch_network_unit('25-macsec.netdev')
3228 networkctl_reload()
3229 self.wait_online('dummy98:degraded', 'macsec99:routable')
3230
3231 def test_nlmon(self):
3232 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
3233 start_networkd()
3234
3235 self.wait_online('nlmon99:carrier')
3236 self.networkctl_check_unit('nlmon99', '25-nlmon', '26-netdev-link-local-addressing-yes')
3237
3238 touch_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
3239 networkctl_reload()
3240 self.wait_online('nlmon99:carrier')
3241
3242 @expectedFailureIfModuleIsNotAvailable('ifb')
3243 def test_ifb(self):
3244 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
3245 start_networkd()
3246
3247 self.wait_online('ifb99:degraded')
3248 self.networkctl_check_unit('ifb99', '25-ifb', '26-netdev-link-local-addressing-yes')
3249
3250 touch_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
3251 networkctl_reload()
3252 self.wait_online('ifb99:degraded')
3253
3254 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3255 def test_rps_cpu_1(self):
3256 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
3257 start_networkd()
3258
3259 self.wait_online('dummy98:carrier')
3260 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-1')
3261
3262 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3263 print(output)
3264 self.assertEqual(int(output.replace(',', ''), base=16), 2)
3265
3266 @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
3267 def test_rps_cpu_0_1(self):
3268 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
3269 start_networkd()
3270
3271 self.wait_online('dummy98:carrier')
3272 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-1')
3273
3274 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3275 print(output)
3276 self.assertEqual(int(output.replace(',', ''), base=16), 3)
3277
3278 @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test")
3279 def test_rps_cpu_multi(self):
3280 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
3281 start_networkd()
3282
3283 self.wait_online('dummy98:carrier')
3284 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-multi')
3285
3286 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3287 print(output)
3288 self.assertEqual(int(output.replace(',', ''), base=16), 15)
3289
3290 def test_rps_cpu(self):
3291 cpu_count = os.cpu_count()
3292
3293 copy_network_unit('12-dummy.netdev', '12-dummy.network')
3294 start_networkd()
3295
3296 self.wait_online('dummy98:carrier')
3297 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy')
3298
3299 # 0
3300 copy_network_unit('25-rps-cpu-0.link')
3301 udevadm_trigger('/sys/class/net/dummy98')
3302 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0')
3303 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3304 print(output)
3305 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3306 remove_network_unit('25-rps-cpu-0.link')
3307
3308 # all
3309 copy_network_unit('25-rps-cpu-all.link')
3310 udevadm_trigger('/sys/class/net/dummy98')
3311 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
3312 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3313 print(output)
3314 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3315 remove_network_unit('25-rps-cpu-all.link')
3316
3317 # disable
3318 copy_network_unit('24-rps-cpu-disable.link')
3319 udevadm_trigger('/sys/class/net/dummy98')
3320 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-disable')
3321 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3322 print(output)
3323 self.assertEqual(int(output.replace(',', ''), base=16), 0)
3324 remove_network_unit('24-rps-cpu-disable.link')
3325
3326 # set all again
3327 copy_network_unit('25-rps-cpu-all.link')
3328 udevadm_trigger('/sys/class/net/dummy98')
3329 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all')
3330 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3331 print(output)
3332 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3333 remove_network_unit('25-rps-cpu-all.link')
3334
3335 # empty -> unchanged
3336 copy_network_unit('24-rps-cpu-empty.link')
3337 udevadm_trigger('/sys/class/net/dummy98')
3338 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-empty')
3339 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3340 print(output)
3341 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3342 remove_network_unit('24-rps-cpu-empty.link')
3343
3344 # 0, then empty -> unchanged
3345 copy_network_unit('25-rps-cpu-0-empty.link')
3346 udevadm_trigger('/sys/class/net/dummy98')
3347 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-empty')
3348 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3349 print(output)
3350 self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
3351 remove_network_unit('25-rps-cpu-0-empty.link')
3352
3353 # 0, then invalid -> 0
3354 copy_network_unit('25-rps-cpu-0-invalid.link')
3355 udevadm_trigger('/sys/class/net/dummy98')
3356 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-invalid')
3357 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3358 print(output)
3359 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3360 remove_network_unit('25-rps-cpu-0-invalid.link')
3361
3362 # invalid -> unchanged
3363 copy_network_unit('24-rps-cpu-invalid.link')
3364 udevadm_trigger('/sys/class/net/dummy98')
3365 self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-invalid')
3366 output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
3367 print(output)
3368 self.assertEqual(int(output.replace(',', ''), base=16), 1)
3369 remove_network_unit('24-rps-cpu-invalid.link')
3370
3371 class NetworkdL2TPTests(unittest.TestCase, Utilities):
3372
3373 def setUp(self):
3374 setup_common()
3375
3376 def tearDown(self):
3377 tear_down_common()
3378
3379 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
3380 def test_l2tp_udp(self):
3381 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3382 '25-l2tp-udp.netdev', '25-l2tp.network')
3383 start_networkd()
3384
3385 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
3386 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3387 self.networkctl_check_unit('l2tp-ses1', '25-l2tp-udp', '25-l2tp')
3388 self.networkctl_check_unit('l2tp-ses2', '25-l2tp-udp', '25-l2tp')
3389
3390 output = check_output('ip l2tp show tunnel tunnel_id 10')
3391 print(output)
3392 self.assertRegex(output, "Tunnel 10, encap UDP")
3393 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3394 self.assertRegex(output, "Peer tunnel 11")
3395 self.assertRegex(output, "UDP source / dest ports: 3000/4000")
3396 self.assertRegex(output, "UDP checksum: enabled")
3397
3398 output = check_output('ip l2tp show session tid 10 session_id 15')
3399 print(output)
3400 self.assertRegex(output, "Session 15 in tunnel 10")
3401 self.assertRegex(output, "Peer session 16, tunnel 11")
3402 self.assertRegex(output, "interface name: l2tp-ses1")
3403
3404 output = check_output('ip l2tp show session tid 10 session_id 17')
3405 print(output)
3406 self.assertRegex(output, "Session 17 in tunnel 10")
3407 self.assertRegex(output, "Peer session 18, tunnel 11")
3408 self.assertRegex(output, "interface name: l2tp-ses2")
3409
3410 touch_network_unit(
3411 '11-dummy.netdev', '25-l2tp-dummy.network',
3412 '25-l2tp-udp.netdev', '25-l2tp.network')
3413 networkctl_reload()
3414 self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
3415
3416 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
3417 def test_l2tp_ip(self):
3418 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
3419 '25-l2tp-ip.netdev', '25-l2tp.network')
3420 start_networkd()
3421
3422 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
3423 self.networkctl_check_unit('test1', '11-dummy', '25-l2tp-dummy')
3424 self.networkctl_check_unit('l2tp-ses3', '25-l2tp-ip', '25-l2tp')
3425 self.networkctl_check_unit('l2tp-ses4', '25-l2tp-ip', '25-l2tp')
3426
3427 output = check_output('ip l2tp show tunnel tunnel_id 10')
3428 print(output)
3429 self.assertRegex(output, "Tunnel 10, encap IP")
3430 self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
3431 self.assertRegex(output, "Peer tunnel 12")
3432
3433 output = check_output('ip l2tp show session tid 10 session_id 25')
3434 print(output)
3435 self.assertRegex(output, "Session 25 in tunnel 10")
3436 self.assertRegex(output, "Peer session 26, tunnel 12")
3437 self.assertRegex(output, "interface name: l2tp-ses3")
3438
3439 output = check_output('ip l2tp show session tid 10 session_id 27')
3440 print(output)
3441 self.assertRegex(output, "Session 27 in tunnel 10")
3442 self.assertRegex(output, "Peer session 28, tunnel 12")
3443 self.assertRegex(output, "interface name: l2tp-ses4")
3444
3445 touch_network_unit(
3446 '11-dummy.netdev', '25-l2tp-dummy.network',
3447 '25-l2tp-ip.netdev', '25-l2tp.network')
3448 networkctl_reload()
3449 self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
3450
3451 class NetworkdNetworkTests(unittest.TestCase, Utilities):
3452
3453 def setUp(self):
3454 setup_common()
3455
3456 def tearDown(self):
3457 tear_down_common()
3458
3459 def test_ID_NET_MANAGED_BY(self):
3460 copy_network_unit('11-dummy.netdev', '11-dummy-unmanaged.link', '11-dummy.network')
3461 start_networkd()
3462 self.wait_online('test1:off', setup_state='unmanaged')
3463
3464 check_output('ip link set dev test1 up')
3465 self.wait_online('test1:degraded', setup_state='unmanaged')
3466
3467 check_output('ip link set dev test1 down')
3468 self.wait_online('test1:off', setup_state='unmanaged')
3469
3470 def verify_address_static(
3471 self,
3472 label1: str,
3473 label2: str,
3474 label3: str,
3475 broadcast1: str,
3476 broadcast2: str,
3477 broadcast3: str,
3478 peer1: str,
3479 peer2: str,
3480 peer3: str,
3481 peer4: str,
3482 peer5: str,
3483 peer6: str,
3484 scope1: str,
3485 scope2: str,
3486 deprecated1: str,
3487 deprecated2: str,
3488 deprecated3: str,
3489 deprecated4: str,
3490 route_metric: int,
3491 flag1: str,
3492 flag2: str,
3493 flag3: str,
3494 flag4: str,
3495 ip4_null_16: str,
3496 ip4_null_24: str,
3497 ip6_null_73: str,
3498 ip6_null_74: str,
3499 ):
3500 output = check_output('ip address show dev dummy98')
3501 print(output)
3502
3503 # simple settings
3504 self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
3505 self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
3506 self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
3507 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
3508 self.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output)
3509 self.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output)
3510
3511 # label
3512 self.assertIn(f'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output)
3513 self.assertIn(f'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output)
3514 self.assertIn(f'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output)
3515
3516 # broadcast
3517 self.assertIn(f'inet 10.4.1.1/24{broadcast1} scope global dummy98', output)
3518 self.assertIn(f'inet 10.4.2.1/24{broadcast2} scope global dummy98', output)
3519 self.assertIn(f'inet 10.4.3.1/24{broadcast3} scope global dummy98', output)
3520
3521 # peer
3522 self.assertIn(f'inet 10.5.1.1{peer1} scope global dummy98', output)
3523 self.assertIn(f'inet 10.5.2.1{peer2} scope global dummy98', output)
3524 self.assertIn(f'inet 10.5.3.1{peer3} scope global dummy98', output)
3525 self.assertIn(f'inet6 2001:db8:0:f103::1{peer4} scope global', output)
3526 self.assertIn(f'inet6 2001:db8:0:f103::2{peer5} scope global', output)
3527 self.assertIn(f'inet6 2001:db8:0:f103::3{peer6} scope global', output)
3528
3529 # scope
3530 self.assertIn(f'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output)
3531 self.assertIn(f'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output)
3532
3533 # lifetime
3534 self.assertIn(f'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output)
3535 self.assertIn(f'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output)
3536 self.assertIn(f'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output)
3537 self.assertIn(f'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output)
3538
3539 # route metric
3540 self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
3541 self.assertRegex(output, rf'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
3542
3543 output_route = check_output('ip -4 route show dev dummy98 10.8.1.0/24')
3544 print(output_route)
3545 self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route)
3546
3547 output_route = check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
3548 print(output_route)
3549 self.assertIn(f'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route)
3550
3551 # flags
3552 self.assertIn(f'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output)
3553 self.assertIn(f'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output)
3554 self.assertIn(f'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output)
3555 self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
3556
3557 # null address
3558 self.assertTrue(ip4_null_16.endswith('.0.1'))
3559 prefix16 = ip4_null_16[:-len('.0.1')]
3560 self.assertTrue(ip4_null_24.endswith('.1'))
3561 prefix24 = ip4_null_24[:-len('.1')]
3562 self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
3563 self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
3564 self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
3565 self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
3566
3567 # invalid sections
3568 self.assertNotIn('10.4.4.1', output)
3569 self.assertNotIn('10.5.4.1', output)
3570 self.assertNotIn('10.5.5.1', output)
3571 self.assertNotIn('10.8.2.1', output)
3572 self.assertNotIn('10.9.3.1', output)
3573 self.assertNotIn('2001:db8:0:f101::2', output)
3574 self.assertNotIn('2001:db8:0:f103::4', output)
3575
3576 # netlabel
3577 self.check_netlabel('dummy98', r'10\.10\.1\.0/24')
3578
3579 check_json(networkctl_json())
3580
3581 @expectedFailureIfKernelReturnsInvalidFlags()
3582 def test_address_static(self):
3583 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
3584 self.setup_nftset('addr4', 'ipv4_addr')
3585 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
3586 self.setup_nftset('ifindex', 'iface_index')
3587 start_networkd()
3588
3589 self.wait_online('dummy98:routable')
3590
3591 ip4_null_16 = None
3592 ip4_null_24 = None
3593 output = check_output('ip -4 --json address show dev dummy98')
3594 for i in json.loads(output)[0]['addr_info']:
3595 if i['label'] == 'subnet16':
3596 ip4_null_16 = i['local']
3597 elif i['label'] == 'subnet24':
3598 ip4_null_24 = i['local']
3599 self.assertTrue(ip4_null_16.endswith('.0.1'))
3600 self.assertTrue(ip4_null_24.endswith('.1'))
3601
3602 ip6_null_73 = None
3603 ip6_null_74 = None
3604 output = check_output('ip -6 --json address show dev dummy98')
3605 for i in json.loads(output)[0]['addr_info']:
3606 if i['prefixlen'] == 73:
3607 ip6_null_73 = i['local']
3608 elif i['prefixlen'] == 74:
3609 ip6_null_74 = i['local']
3610 self.assertTrue(ip6_null_73.endswith(':1'))
3611 self.assertTrue(ip6_null_74.endswith(':1'))
3612
3613 self.verify_address_static(
3614 label1='label1',
3615 label2='label2',
3616 label3='dummy98',
3617 broadcast1='',
3618 broadcast2=' brd 10.4.2.255',
3619 broadcast3=' brd 10.4.3.63',
3620 peer1=' peer 10.5.1.101/24',
3621 peer2=' peer 10.5.2.101/24',
3622 peer3='/24 brd 10.5.3.255',
3623 peer4=' peer 2001:db8:0:f103::101/128',
3624 peer5=' peer 2001:db8:0:f103::102/128',
3625 peer6='/128',
3626 scope1='global',
3627 scope2='link',
3628 deprecated1='',
3629 deprecated2=' deprecated',
3630 deprecated3='',
3631 deprecated4=' deprecated',
3632 route_metric=128,
3633 flag1=' noprefixroute',
3634 flag2='',
3635 flag3=' noprefixroute',
3636 flag4=' home mngtmpaddr',
3637 ip4_null_16=ip4_null_16,
3638 ip4_null_24=ip4_null_24,
3639 ip6_null_73=ip6_null_73,
3640 ip6_null_74=ip6_null_74,
3641 )
3642 # nft set
3643 self.check_nftset('addr4', r'10\.10\.1\.1')
3644 self.check_nftset('network4', r'10\.10\.1\.0/24')
3645 self.check_nftset('ifindex', 'dummy98')
3646
3647 self.teardown_nftset('addr4', 'network4', 'ifindex')
3648
3649 copy_network_unit('25-address-static.network.d/10-override.conf')
3650 networkctl_reload()
3651 self.wait_online('dummy98:routable')
3652 self.verify_address_static(
3653 label1='new-label1',
3654 label2='dummy98',
3655 label3='new-label3',
3656 broadcast1=' brd 10.4.1.255',
3657 broadcast2='',
3658 broadcast3=' brd 10.4.3.31',
3659 peer1=' peer 10.5.1.102/24',
3660 peer2='/24 brd 10.5.2.255',
3661 peer3=' peer 10.5.3.102/24',
3662 peer4=' peer 2001:db8:0:f103::201/128',
3663 peer5='/128',
3664 peer6=' peer 2001:db8:0:f103::203/128',
3665 scope1='link',
3666 scope2='global',
3667 deprecated1=' deprecated',
3668 deprecated2='',
3669 deprecated3=' deprecated',
3670 deprecated4='',
3671 route_metric=256,
3672 flag1='',
3673 flag2=' noprefixroute',
3674 flag3=' home mngtmpaddr',
3675 flag4=' noprefixroute',
3676 ip4_null_16=ip4_null_16,
3677 ip4_null_24=ip4_null_24,
3678 ip6_null_73=ip6_null_73,
3679 ip6_null_74=ip6_null_74,
3680 )
3681
3682 networkctl_reconfigure('dummy98')
3683 self.wait_online('dummy98:routable')
3684 self.verify_address_static(
3685 label1='new-label1',
3686 label2='dummy98',
3687 label3='new-label3',
3688 broadcast1=' brd 10.4.1.255',
3689 broadcast2='',
3690 broadcast3=' brd 10.4.3.31',
3691 peer1=' peer 10.5.1.102/24',
3692 peer2='/24 brd 10.5.2.255',
3693 peer3=' peer 10.5.3.102/24',
3694 peer4=' peer 2001:db8:0:f103::201/128',
3695 peer5='/128',
3696 peer6=' peer 2001:db8:0:f103::203/128',
3697 scope1='link',
3698 scope2='global',
3699 deprecated1=' deprecated',
3700 deprecated2='',
3701 deprecated3=' deprecated',
3702 deprecated4='',
3703 route_metric=256,
3704 flag1='',
3705 flag2=' noprefixroute',
3706 flag3=' home mngtmpaddr',
3707 flag4=' noprefixroute',
3708 ip4_null_16=ip4_null_16,
3709 ip4_null_24=ip4_null_24,
3710 ip6_null_73=ip6_null_73,
3711 ip6_null_74=ip6_null_74,
3712 )
3713
3714 # Tests for #20891.
3715 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
3716 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
3717 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
3718 output = check_output('ip address show dev dummy98')
3719 print(output)
3720 self.assertNotRegex(output, '10.7.1.1/24 .* deprecated')
3721 self.assertNotRegex(output, '2001:db8:0:f104::1/64 .* deprecated')
3722
3723 # 2. reconfigure the interface, and check the deprecated flag is set again
3724 networkctl_reconfigure('dummy98')
3725 self.wait_online('dummy98:routable')
3726 self.verify_address_static(
3727 label1='new-label1',
3728 label2='dummy98',
3729 label3='new-label3',
3730 broadcast1=' brd 10.4.1.255',
3731 broadcast2='',
3732 broadcast3=' brd 10.4.3.31',
3733 peer1=' peer 10.5.1.102/24',
3734 peer2='/24 brd 10.5.2.255',
3735 peer3=' peer 10.5.3.102/24',
3736 peer4=' peer 2001:db8:0:f103::201/128',
3737 peer5='/128',
3738 peer6=' peer 2001:db8:0:f103::203/128',
3739 scope1='link',
3740 scope2='global',
3741 deprecated1=' deprecated',
3742 deprecated2='',
3743 deprecated3=' deprecated',
3744 deprecated4='',
3745 route_metric=256,
3746 flag1='',
3747 flag2=' noprefixroute',
3748 flag3=' home mngtmpaddr',
3749 flag4=' noprefixroute',
3750 ip4_null_16=ip4_null_16,
3751 ip4_null_24=ip4_null_24,
3752 ip6_null_73=ip6_null_73,
3753 ip6_null_74=ip6_null_74,
3754 )
3755
3756 # test for ENOBUFS issue #17012 (with reload)
3757 copy_network_unit('25-address-static.network.d/10-many-address.conf')
3758 networkctl_reload()
3759 self.wait_online('dummy98:routable')
3760 output = check_output('ip -4 address show dev dummy98')
3761 for i in range(1, 254):
3762 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3763
3764 # (with reconfigure)
3765 networkctl_reconfigure('dummy98')
3766 self.wait_online('dummy98:routable')
3767 output = check_output('ip -4 address show dev dummy98')
3768 for i in range(1, 254):
3769 self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3770
3771 # test for an empty string assignment for Address= in [Network]
3772 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
3773 networkctl_reload()
3774 self.wait_online('dummy98:routable')
3775 output = check_output('ip -4 address show dev dummy98')
3776 for i in range(1, 254):
3777 self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
3778 self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
3779
3780 def test_address_ipv4acd(self):
3781 check_output('ip netns add ns99')
3782 check_output('ip link add veth99 type veth peer veth-peer')
3783 check_output('ip link set veth-peer netns ns99')
3784 check_output('ip link set veth99 up')
3785 check_output('ip netns exec ns99 ip link set veth-peer up')
3786 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
3787
3788 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False)
3789 start_networkd()
3790 self.wait_online('veth99:routable')
3791
3792 output = check_output('ip -4 address show dev veth99')
3793 print(output)
3794 self.assertNotIn('192.168.100.10/24', output)
3795 self.assertIn('192.168.100.11/24', output)
3796
3797 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
3798 networkctl_reload()
3799 self.wait_operstate('veth99', operstate='routable', setup_state='configuring', setup_timeout=10)
3800
3801 output = check_output('ip -4 address show dev veth99')
3802 print(output)
3803 self.assertNotIn('192.168.100.10/24', output)
3804 self.assertIn('192.168.100.11/24', output)
3805
3806 def test_address_peer_ipv4(self):
3807 # test for issue #17304
3808 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
3809
3810 for trial in range(2):
3811 if trial == 0:
3812 start_networkd()
3813 else:
3814 restart_networkd()
3815
3816 self.wait_online('dummy98:routable')
3817
3818 output = check_output('ip -4 address show dev dummy98')
3819 self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
3820
3821 @expectedFailureIfModuleIsNotAvailable('vrf')
3822 def test_prefix_route(self):
3823 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3824 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3825 '25-vrf.netdev', '25-vrf.network')
3826 for trial in range(2):
3827 if trial == 0:
3828 start_networkd()
3829 else:
3830 restart_networkd()
3831
3832 self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
3833
3834 output = check_output('ip route show table 42 dev dummy98')
3835 print('### ip route show table 42 dev dummy98')
3836 print(output)
3837 self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
3838 self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3839 self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3840 self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3841 self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
3842 self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3843 self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3844 output = check_output('ip -6 route show table 42 dev dummy98')
3845 print('### ip -6 route show table 42 dev dummy98')
3846 print(output)
3847 if trial == 0:
3848 # Kernel's bug?
3849 self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3850 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3851 self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3852 self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3853 self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3854 self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3855 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3856 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3857
3858 print()
3859
3860 output = check_output('ip route show dev test1')
3861 print('### ip route show dev test1')
3862 print(output)
3863 self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3864 output = check_output('ip route show table local dev test1')
3865 print('### ip route show table local dev test1')
3866 print(output)
3867 self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
3868 self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3869 self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3870 self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
3871 self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3872 self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3873 output = check_output('ip -6 route show dev test1')
3874 print('### ip -6 route show dev test1')
3875 print(output)
3876 self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3877 self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3878 self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
3879 output = check_output('ip -6 route show table local dev test1')
3880 print('### ip -6 route show table local dev test1')
3881 print(output)
3882 self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3883 self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3884 self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3885 self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
3886 self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3887
3888 def test_configure_without_carrier(self):
3889 copy_network_unit('11-dummy.netdev')
3890 start_networkd()
3891 self.wait_operstate('test1', 'off', '')
3892 check_output('ip link set dev test1 up carrier off')
3893
3894 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False)
3895 restart_networkd()
3896 self.wait_online('test1:no-carrier')
3897
3898 carrier_map = {'on': '1', 'off': '0'}
3899 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3900 for carrier in ['off', 'on', 'off']:
3901 with self.subTest(carrier=carrier):
3902 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3903 check_output(f'ip link set dev test1 carrier {carrier}')
3904 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3905
3906 output = networkctl_status('test1')
3907 print(output)
3908 self.assertRegex(output, '192.168.0.15')
3909 self.assertRegex(output, '192.168.0.1')
3910 self.assertRegex(output, routable_map[carrier])
3911
3912 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
3913 copy_network_unit('11-dummy.netdev')
3914 start_networkd()
3915 self.wait_operstate('test1', 'off', '')
3916 check_output('ip link set dev test1 up carrier off')
3917
3918 copy_network_unit('25-test1.network')
3919 restart_networkd()
3920 self.wait_online('test1:no-carrier')
3921
3922 carrier_map = {'on': '1', 'off': '0'}
3923 routable_map = {'on': 'routable', 'off': 'no-carrier'}
3924 for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
3925 with self.subTest(carrier=carrier, have_config=have_config):
3926 if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
3927 check_output(f'ip link set dev test1 carrier {carrier}')
3928 self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3929
3930 output = networkctl_status('test1')
3931 print(output)
3932 if have_config:
3933 self.assertRegex(output, '192.168.0.15')
3934 self.assertRegex(output, '192.168.0.1')
3935 else:
3936 self.assertNotRegex(output, '192.168.0.15')
3937 self.assertNotRegex(output, '192.168.0.1')
3938 self.assertRegex(output, routable_map[carrier])
3939
3940 def check_routing_policy_rule_test1(self):
3941 print('### Checking routing policy rules requested by test1')
3942
3943 output = check_output('ip rule list iif test1 priority 111')
3944 print(output)
3945 self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3946
3947 output = check_output('ip -6 rule list iif test1 priority 100')
3948 print(output)
3949 self.assertIn('100: from all iif test1 lookup 8', output)
3950
3951 output = check_output('ip rule list iif test1 priority 101')
3952 print(output)
3953 self.assertIn('101: from all iif test1 lookup 9', output)
3954
3955 output = check_output('ip -6 rule list iif test1 priority 101')
3956 print(output)
3957 self.assertIn('101: from all iif test1 lookup 9', output)
3958
3959 output = check_output('ip rule list iif test1 priority 102')
3960 print(output)
3961 self.assertIn('102: from 0.0.0.0/8 iif test1 lookup 10', output)
3962
3963 output = check_output('ip rule list iif test1 priority 103')
3964 print(output)
3965 self.assertIn('103: from 10.0.0.0/16 iif test1 lookup 11 goto 111', output)
3966
3967 output = check_output('ip rule list iif test1 priority 104')
3968 print(output)
3969 self.assertIn('104: from 10.1.0.0/16 iif test1 lookup 12 nop', output)
3970
3971 output = check_output('ip rule list iif test1 priority 200')
3972 print(output)
3973 self.assertIn('200: from all fwmark 0/0x1 iif test1 lookup 20', output)
3974
3975 output = check_output('ip rule list iif test1 priority 201')
3976 print(output)
3977 self.assertIn('201: from all fwmark 0x7/0xff iif test1 lookup 21', output)
3978
3979 output = check_output('ip rule list iif test1 priority 202')
3980 print(output)
3981 self.assertIn('202: from all fwmark 0x270f iif test1 lookup 22', output)
3982
3983 output = check_output('ip rule list to 192.0.2.0/26')
3984 print(output)
3985 self.assertIn('to 192.0.2.0/26 lookup 1001', output)
3986
3987 output = check_output('ip rule list to 192.0.2.64/26')
3988 print(output)
3989 self.assertIn('to 192.0.2.64/26 lookup 1001', output)
3990
3991 output = check_output('ip rule list to 192.0.2.128/26')
3992 print(output)
3993 self.assertIn('to 192.0.2.128/26 lookup 1001', output)
3994
3995 output = check_output('ip rule list to 192.0.2.192/26')
3996 print(output)
3997 self.assertIn('to 192.0.2.192/26 lookup 1001', output)
3998
3999 def check_routing_policy_rule_dummy98(self):
4000 print('### Checking routing policy rules requested by dummy98')
4001
4002 output = check_output('ip rule list priority 112')
4003 print(output)
4004 self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
4005
4006 def _test_routing_policy_rule(self, manage_foreign_routes):
4007 if not manage_foreign_routes:
4008 copy_networkd_conf_dropin('networkd-manage-foreign-rules-no.conf')
4009 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
4010
4011 stop_networkd()
4012
4013 check_output('ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16')
4014 check_output('ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64')
4015
4016 start_networkd()
4017 self.wait_online('test1:degraded')
4018
4019 self.check_routing_policy_rule_test1()
4020 check_json(networkctl_json())
4021
4022 output = check_output('ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16')
4023 print(output)
4024 if manage_foreign_routes:
4025 self.assertEqual(output, '')
4026 else:
4027 self.assertIn(output, '20001: from 10.10.0.0/16 lookup 9999')
4028 check_output('ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16')
4029
4030 output = check_output('ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64')
4031 print(output)
4032 if manage_foreign_routes:
4033 self.assertEqual(output, '')
4034 else:
4035 self.assertIn(output, '20001: from 2001:db8:0:1::/64 lookup 9999')
4036 check_output('ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64')
4037
4038 def test_routing_policy_rule(self):
4039 first = True
4040 for manage_foreign_routes in [True, False]:
4041 if first:
4042 first = False
4043 else:
4044 self.tearDown()
4045
4046 print(f'### test_routing_policy_rule(manage_foreign_routes={manage_foreign_routes})')
4047 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4048 self._test_routing_policy_rule(manage_foreign_routes)
4049
4050 def test_routing_policy_rule_restart_and_reconfigure(self):
4051 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
4052 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
4053
4054 # For #11280 and #34068.
4055
4056 for trial in range(3):
4057 restart_networkd(show_logs=(trial > 0))
4058 self.wait_online('test1:degraded', 'dummy98:degraded')
4059
4060 self.check_routing_policy_rule_test1()
4061 self.check_routing_policy_rule_dummy98()
4062
4063 networkctl_reconfigure('test1')
4064 self.wait_online('test1:degraded')
4065
4066 self.check_routing_policy_rule_test1()
4067 self.check_routing_policy_rule_dummy98()
4068
4069 networkctl_reconfigure('dummy98')
4070 self.wait_online('dummy98:degraded')
4071
4072 self.check_routing_policy_rule_test1()
4073 self.check_routing_policy_rule_dummy98()
4074
4075 def test_routing_policy_rule_reconfigure(self):
4076 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
4077 start_networkd()
4078 self.wait_online('test1:degraded')
4079
4080 output = check_output('ip rule list table 1011')
4081 print(output)
4082 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4083 self.assertIn('10112: from all oif test1 lookup 1011', output)
4084 self.assertIn('10113: from all iif test1 lookup 1011', output)
4085 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4086
4087 output = check_output('ip -6 rule list table 1011')
4088 print(output)
4089 self.assertIn('10112: from all oif test1 lookup 1011', output)
4090
4091 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
4092 networkctl_reload()
4093 self.wait_online('test1:degraded')
4094
4095 output = check_output('ip rule list table 1011')
4096 print(output)
4097 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4098 self.assertIn('10112: from all oif test1 lookup 1011', output)
4099 self.assertIn('10113: from all iif test1 lookup 1011', output)
4100 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4101
4102 output = check_output('ip -6 rule list table 1011')
4103 print(output)
4104 self.assertNotIn('10112: from all oif test1 lookup 1011', output)
4105 self.assertIn('10113: from all iif test1 lookup 1011', output)
4106
4107 call('ip rule delete priority 10111')
4108 call('ip rule delete priority 10112')
4109 call('ip rule delete priority 10113')
4110 call('ip rule delete priority 10114')
4111 call('ip -6 rule delete priority 10113')
4112
4113 output = check_output('ip rule list table 1011')
4114 print(output)
4115 self.assertEqual(output, '')
4116
4117 output = check_output('ip -6 rule list table 1011')
4118 print(output)
4119 self.assertEqual(output, '')
4120
4121 networkctl_reconfigure('test1')
4122 self.wait_online('test1:degraded')
4123
4124 output = check_output('ip rule list table 1011')
4125 print(output)
4126 self.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output)
4127 self.assertIn('10112: from all oif test1 lookup 1011', output)
4128 self.assertIn('10113: from all iif test1 lookup 1011', output)
4129 self.assertIn('10114: from 192.168.8.254 lookup 1011', output)
4130
4131 output = check_output('ip -6 rule list table 1011')
4132 print(output)
4133 self.assertIn('10113: from all iif test1 lookup 1011', output)
4134
4135 def test_routing_policy_rule_manual(self):
4136 # For issue #36244.
4137 copy_network_unit(
4138 '11-dummy.netdev',
4139 '25-routing-policy-rule-manual.network')
4140 start_networkd()
4141 self.wait_operstate('test1', operstate='off', setup_state='configuring', setup_timeout=20)
4142
4143 check_output('ip link add test2 type dummy')
4144 self.wait_operstate('test2', operstate='off', setup_state='configuring', setup_timeout=20)
4145
4146 networkctl('up', 'test2')
4147 self.wait_online('test2:degraded')
4148
4149 # The request for the routing policy rules are bound to test1. Hence, we need to wait for the rules
4150 # being configured explicitly.
4151 for _ in range(20):
4152 time.sleep(0.5)
4153
4154 output = check_output('ip -4 rule list table 51819')
4155 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4156 continue
4157
4158 output = check_output('ip -6 rule list table 51819')
4159 if output != '10: from all lookup 51819 suppress_prefixlength 0 proto static':
4160 continue
4161
4162 output = check_output('ip -4 rule list table 51820')
4163 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4164 continue
4165
4166 output = check_output('ip -6 rule list table 51820')
4167 if output != '11: not from all fwmark 0x38f lookup 51820 proto static':
4168 continue
4169
4170 break
4171 else:
4172 self.assertFalse(True)
4173
4174 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
4175 def test_routing_policy_rule_port_range(self):
4176 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
4177 start_networkd()
4178 self.wait_online('test1:degraded')
4179
4180 output = check_output('ip rule')
4181 print(output)
4182 self.assertIn('111:', output)
4183 self.assertIn('from 192.168.100.18 ', output)
4184 self.assertIn('sport 1123-1150 ', output)
4185 self.assertIn('dport 3224-3290 ', output)
4186 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4187 self.assertIn('lookup 7 ', output)
4188
4189 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
4190 def test_routing_policy_rule_invert(self):
4191 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
4192 start_networkd()
4193 self.wait_online('test1:degraded')
4194
4195 output = check_output('ip rule')
4196 print(output)
4197 self.assertIn('111:', output)
4198 self.assertIn('not ', output)
4199 self.assertIn('from 192.168.100.18 ', output)
4200 self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
4201 self.assertIn('lookup 7 ', output)
4202
4203 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
4204 def test_routing_policy_rule_l3mdev(self):
4205 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
4206 start_networkd()
4207 self.wait_online('test1:degraded')
4208
4209 output = check_output('ip rule')
4210 print(output)
4211 self.assertIn('1500: from all lookup [l3mdev-table]', output)
4212 self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
4213
4214 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
4215 def test_routing_policy_rule_uidrange(self):
4216 copy_network_unit('25-fibrule-uidrange.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('111:', output)
4223 self.assertIn('from 192.168.100.18 ', output)
4224 self.assertIn('lookup 7 ', output)
4225 self.assertIn('uidrange 100-200 ', output)
4226
4227 def _check_route_static(self, test1_is_managed: bool):
4228 output = networkctl_status('dummy98')
4229 print(output)
4230 output = networkctl_status('test1')
4231 print(output)
4232
4233 print('### ip -6 route show dev dummy98')
4234 output = check_output('ip -6 route show dev dummy98')
4235 print(output)
4236 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4237 self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
4238 self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
4239
4240 print('### ip -6 route show default')
4241 output = check_output('ip -6 route show default')
4242 print(output)
4243 self.assertIn('default', output)
4244 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output)
4245
4246 print('### ip -4 route show dev dummy98')
4247 output = check_output('ip -4 route show dev dummy98')
4248 print(output)
4249 self.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output)
4250 self.assertIn('149.10.124.64 proto static scope link', output)
4251 self.assertIn('169.254.0.0/16 proto static scope link metric 2048', output)
4252 self.assertIn('192.168.1.1 proto static scope link initcwnd 20', output)
4253 self.assertIn('192.168.1.2 proto static scope link initrwnd 30', output)
4254 self.assertIn('192.168.1.3 proto static scope link advmss 30', output)
4255 self.assertIn('192.168.1.4 proto static scope link hoplimit 122', output)
4256 self.assertIn('multicast 149.10.123.4 proto static', output)
4257
4258 print('### ip -4 route show dev dummy98 default')
4259 output = check_output('ip -4 route show dev dummy98 default')
4260 print(output)
4261 self.assertIn('default via 149.10.125.65 proto static onlink', output)
4262 self.assertIn('default via 149.10.124.64 proto static', output)
4263 self.assertIn('default proto static', output)
4264 self.assertIn('default via 1.1.8.104 proto static', output)
4265
4266 print('### ip -4 route show table local dev dummy98')
4267 output = check_output('ip -4 route show table local dev dummy98')
4268 print(output)
4269 self.assertIn('local 149.10.123.1 proto static scope host', output)
4270 self.assertIn('anycast 149.10.123.2 proto static scope link', output)
4271 self.assertIn('broadcast 149.10.123.3 proto static scope link', output)
4272
4273 print('### ip -4 route show type blackhole')
4274 output = check_output('ip -4 route show type blackhole')
4275 print(output)
4276 self.assertIn('blackhole 202.54.1.2 proto static', output)
4277
4278 print('### ip -4 route show type unreachable')
4279 output = check_output('ip -4 route show type unreachable')
4280 print(output)
4281 self.assertIn('unreachable 202.54.1.3 proto static', output)
4282
4283 print('### ip -4 route show type prohibit')
4284 output = check_output('ip -4 route show type prohibit')
4285 print(output)
4286 self.assertIn('prohibit 202.54.1.4 proto static', output)
4287
4288 print('### ip -6 route show type blackhole')
4289 output = check_output('ip -6 route show type blackhole')
4290 print(output)
4291 self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
4292
4293 print('### ip -6 route show type unreachable')
4294 output = check_output('ip -6 route show type unreachable')
4295 print(output)
4296 self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
4297
4298 print('### ip -6 route show type prohibit')
4299 output = check_output('ip -6 route show type prohibit')
4300 print(output)
4301 self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
4302
4303 print('### ip route show 192.168.10.1')
4304 output = check_output('ip route show 192.168.10.1')
4305 print(output)
4306 self.assertIn('192.168.10.1 proto static', output)
4307 self.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output)
4308 self.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output)
4309 self.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output)
4310 self.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output)
4311
4312 print('### ip route show 192.168.10.2')
4313 output = check_output('ip route show 192.168.10.2')
4314 print(output)
4315 # old ip command does not show IPv6 gateways...
4316 self.assertIn('192.168.10.2 proto static', output)
4317 self.assertIn('nexthop', output)
4318 self.assertIn('dev test1 weight 20', output)
4319 self.assertIn('dev test1 weight 30', output)
4320 self.assertIn('dev dummy98 weight 10', output)
4321 self.assertIn('dev dummy98 weight 5', output)
4322
4323 print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
4324 output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
4325 print(output)
4326 # old ip command does not show 'nexthop' keyword and weight...
4327 self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output)
4328 if test1_is_managed:
4329 self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output)
4330 self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output)
4331 self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
4332 self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
4333
4334 check_json(networkctl_json())
4335
4336 def _check_unreachable_routes_removed(self):
4337 print('### ip -4 route show type blackhole')
4338 output = check_output('ip -4 route show type blackhole')
4339 print(output)
4340 self.assertEqual(output, '')
4341
4342 print('### ip -4 route show type unreachable')
4343 output = check_output('ip -4 route show type unreachable')
4344 print(output)
4345 self.assertEqual(output, '')
4346
4347 print('### ip -4 route show type prohibit')
4348 output = check_output('ip -4 route show type prohibit')
4349 print(output)
4350 self.assertEqual(output, '')
4351
4352 print('### ip -6 route show type blackhole')
4353 output = check_output('ip -6 route show type blackhole')
4354 print(output)
4355 self.assertEqual(output, '')
4356
4357 print('### ip -6 route show type unreachable')
4358 output = check_output('ip -6 route show type unreachable')
4359 print(output)
4360 self.assertEqual(output, '')
4361
4362 print('### ip -6 route show type prohibit')
4363 output = check_output('ip -6 route show type prohibit')
4364 print(output)
4365 self.assertEqual(output, '')
4366
4367 check_json(networkctl_json())
4368
4369 def _test_route_static(self, manage_foreign_routes):
4370 if not manage_foreign_routes:
4371 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
4372
4373 copy_network_unit('25-route-static.network', '12-dummy.netdev',
4374 '25-route-static-test1.network', '11-dummy.netdev')
4375 start_networkd()
4376 self.wait_online('dummy98:routable', 'test1:routable')
4377 self._check_route_static(test1_is_managed=True)
4378
4379 # unmanaging test1
4380 remove_network_unit('25-route-static-test1.network')
4381 networkctl_reload()
4382 self.wait_online('dummy98:routable')
4383 self.wait_operstate('test1', setup_state='unmanaged')
4384 self._check_route_static(test1_is_managed=False)
4385
4386 # managing test1 again
4387 copy_network_unit('25-route-static-test1.network')
4388 networkctl_reload()
4389 self.wait_online('dummy98:routable', 'test1:routable')
4390 self._check_route_static(test1_is_managed=True)
4391
4392 # adding an unmanaged interface
4393 check_output('ip link add test2 type dummy')
4394 self.wait_operstate('test2', operstate='off', setup_state='unmanaged')
4395 self._check_route_static(test1_is_managed=True)
4396
4397 # reconfiguring with another config, and check all routes managed by Manager are removed
4398 copy_network_unit('25-address-static.network', copy_dropins=False)
4399 networkctl_reload()
4400 self.wait_online('dummy98:routable')
4401 self._check_unreachable_routes_removed()
4402
4403 # reconfiguring the original config again
4404 remove_network_unit('25-address-static.network')
4405 networkctl_reload()
4406 self.wait_online('dummy98:routable')
4407 self._check_route_static(test1_is_managed=True)
4408
4409 # removing interface, and check all routes managed by Manager are removed
4410 remove_link('dummy98')
4411 time.sleep(2)
4412 self._check_unreachable_routes_removed()
4413
4414 def test_route_static(self):
4415 first = True
4416 for manage_foreign_routes in [True, False]:
4417 if first:
4418 first = False
4419 else:
4420 self.tearDown()
4421
4422 print(f'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
4423 with self.subTest(manage_foreign_routes=manage_foreign_routes):
4424 self._test_route_static(manage_foreign_routes)
4425
4426 def test_route_static_issue_35047(self):
4427 copy_network_unit(
4428 '25-route-static-issue-35047.network',
4429 '25-route-static-issue-35047.network.d/step1.conf',
4430 '12-dummy.netdev',
4431 copy_dropins=False)
4432 start_networkd()
4433 self.wait_online('dummy98:routable')
4434
4435 print('### ip -4 route show table all dev dummy98')
4436 output = check_output('ip -4 route show table all dev dummy98')
4437 print(output)
4438 self.assertIn('192.0.2.2 proto kernel scope link src 192.0.2.1', output)
4439 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4440 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4441
4442 check_output('ip link set dev dummy98 down')
4443 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)
4444 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)
4445 self.wait_route_dropped('dummy98', '198.51.100.0/24 via 192.0.2.2 proto static', ipv='-4', table='all', timeout_sec=10)
4446
4447 print('### ip -4 route show table all dev dummy98')
4448 output = check_output('ip -4 route show table all dev dummy98')
4449 print(output)
4450 self.assertNotIn('192.0.2.2', output)
4451 self.assertNotIn('192.0.2.1', output)
4452 self.assertNotIn('198.51.100.0/24', output)
4453
4454 remove_network_unit('25-route-static-issue-35047.network.d/step1.conf')
4455 copy_network_unit('25-route-static-issue-35047.network.d/step2.conf')
4456 networkctl_reload()
4457 self.wait_online('dummy98:routable')
4458
4459 print('### ip -4 route show table all dev dummy98')
4460 output = check_output('ip -4 route show table all dev dummy98')
4461 print(output)
4462 self.assertIn('192.0.2.2 proto static scope link', output)
4463 self.assertIn('local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', output)
4464 self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output)
4465
4466 def test_route_static_issue_37714(self):
4467 copy_network_unit('12-dummy.netdev', '25-route-static-issue-37714.network')
4468 start_networkd()
4469 self.wait_online('dummy98:routable')
4470
4471 print('### ip -4 rule list table 249')
4472 output = check_output('ip -4 rule list table 249')
4473 print(output)
4474 self.assertIn('32765: from 192.168.0.227 lookup 249 proto static', output)
4475
4476 print('### ip -6 rule list table 249')
4477 output = check_output('ip -6 rule list table 249')
4478 print(output)
4479 self.assertIn('32765: from 2000:f00::227 lookup 249 proto static', output)
4480
4481 print('### ip -4 route show table all dev dummy98')
4482 output = check_output('ip -4 route show table all dev dummy98')
4483 print(output)
4484 self.assertIn('default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink', output)
4485 self.assertIn('192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128', output)
4486 self.assertIn('10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4487 self.assertIn('192.168.0.72 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output)
4488 self.assertIn('192.168.0.193 proto static scope link src 192.168.0.227 metric 128', output)
4489 self.assertIn('local 192.168.0.227 table local proto kernel scope host src 192.168.0.227', output)
4490 self.assertIn('broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227', output)
4491
4492 print('### ip -6 route show table all dev dummy98')
4493 output = check_output('ip -6 route show table all dev dummy98')
4494 print(output)
4495 self.assertIn('2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium', output)
4496 self.assertIn('default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium', output)
4497 self.assertIn('fe80::/64 proto kernel metric 256 pref medium', output)
4498 self.assertIn('local 2000:f00::227 table local proto kernel metric 0 pref medium', output)
4499 self.assertRegex(output, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium', output)
4500 self.assertIn('multicast ff00::/8 table local proto kernel metric 256 pref medium', output)
4501
4502 def test_route_via_ipv6(self):
4503 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
4504 start_networkd()
4505 self.wait_online('dummy98:routable')
4506
4507 output = networkctl_status('dummy98')
4508 print(output)
4509
4510 print('### ip -6 route show dev dummy98')
4511 output = check_output('ip -6 route show dev dummy98')
4512 print(output)
4513 self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
4514 self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
4515
4516 print('### ip -4 route show dev dummy98')
4517 output = check_output('ip -4 route show dev dummy98')
4518 print(output)
4519 self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
4520 self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
4521
4522 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
4523 def test_route_congctl(self):
4524 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
4525 start_networkd()
4526 self.wait_online('dummy98:routable')
4527
4528 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4529 output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
4530 print(output)
4531 self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
4532 self.assertIn('congctl dctcp', output)
4533
4534 print('### ip -4 route show dev dummy98 149.10.124.66')
4535 output = check_output('ip -4 route show dev dummy98 149.10.124.66')
4536 print(output)
4537 self.assertIn('149.10.124.66 proto static', output)
4538 self.assertIn('congctl dctcp', output)
4539 self.assertIn('rto_min 300s', output)
4540
4541 @expectedFailureIfModuleIsNotAvailable('vrf')
4542 def test_route_vrf(self):
4543 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
4544 '25-vrf.netdev', '25-vrf.network')
4545 start_networkd()
4546 self.wait_online('dummy98:routable', 'vrf99:carrier')
4547
4548 output = check_output('ip route show vrf vrf99')
4549 print(output)
4550 self.assertRegex(output, 'default via 192.168.100.1')
4551
4552 output = check_output('ip route show')
4553 print(output)
4554 self.assertNotRegex(output, 'default via 192.168.100.1')
4555
4556 def test_gateway_reconfigure(self):
4557 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
4558 start_networkd()
4559 self.wait_online('dummy98:routable')
4560 print('### ip -4 route show dev dummy98 default')
4561 output = check_output('ip -4 route show dev dummy98 default')
4562 print(output)
4563 self.assertIn('default via 149.10.124.59 proto static', output)
4564 self.assertNotIn('149.10.124.60', output)
4565
4566 remove_network_unit('25-gateway-static.network')
4567 copy_network_unit('25-gateway-next-static.network')
4568 networkctl_reload()
4569 self.wait_online('dummy98:routable')
4570 print('### ip -4 route show dev dummy98 default')
4571 output = check_output('ip -4 route show dev dummy98 default')
4572 print(output)
4573 self.assertNotIn('149.10.124.59', output)
4574 self.assertIn('default via 149.10.124.60 proto static', output)
4575
4576 def test_ip_route_ipv6_src_route(self):
4577 # a dummy device does not make the addresses go through tentative state, so we
4578 # reuse a bond from an earlier test, which does make the addresses go through
4579 # tentative state, and do our test on that
4580 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4581 start_networkd()
4582 self.wait_online('dummy98:enslaved', 'bond199:routable')
4583
4584 output = check_output('ip -6 route list dev bond199')
4585 print(output)
4586 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output)
4587
4588 def test_route_preferred_source_with_existing_address(self):
4589 # See issue #28009.
4590 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
4591 start_networkd()
4592
4593 for i in range(3):
4594 if i != 0:
4595 networkctl_reconfigure('dummy98')
4596
4597 self.wait_online('dummy98:routable')
4598
4599 output = check_output('ip -6 route list dev dummy98')
4600 print(output)
4601 self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output)
4602
4603 output = check_output('ip -4 route list dev dummy98')
4604 print(output)
4605 self.assertIn('10.123.0.0/16 via 192.168.30.1 proto static src 10.10.10.1', output)
4606
4607 def test_ip_link_mac_address(self):
4608 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
4609 start_networkd()
4610 self.wait_online('dummy98:degraded')
4611
4612 output = check_output('ip link show dummy98')
4613 print(output)
4614 self.assertRegex(output, '00:01:02:aa:bb:cc')
4615
4616 def test_ip_link_unmanaged(self):
4617 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
4618 start_networkd()
4619
4620 self.wait_operstate('dummy98', 'off', setup_state='unmanaged')
4621
4622 def test_ipv6_address_label(self):
4623 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
4624 copy_networkd_conf_dropin('networkd-address-label.conf')
4625 start_networkd()
4626 self.wait_online('dummy98:degraded')
4627
4628 output = check_output('ip addrlabel list')
4629 print(output)
4630 self.assertRegex(output, '2004:da8:1::/64 dev dummy98 label 4444')
4631 self.assertRegex(output, '2004:da8:2::/64 label 5555')
4632
4633 def test_ipv6_proxy_ndp(self):
4634 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
4635 start_networkd()
4636
4637 self.wait_online('dummy98:routable')
4638
4639 output = check_output('ip neighbor show proxy dev dummy98')
4640 print(output)
4641 for i in range(1, 5):
4642 self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
4643
4644 def test_ipv6_neigh_retrans_time(self):
4645 link='test25'
4646 copy_network_unit('25-dummy.netdev', '25-dummy.network')
4647 start_networkd()
4648
4649 self.wait_online(f'{link}:degraded')
4650 remove_network_unit('25-dummy.network')
4651
4652 # expect retrans_time_ms updated
4653 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4654 networkctl_reload()
4655 self.wait_online(f'{link}:degraded')
4656 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4657 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
4658
4659 # expect retrans_time_ms unchanged
4660 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4661 networkctl_reload()
4662 self.wait_online(f'{link}:degraded')
4663 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4664 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
4665
4666 # expect retrans_time_ms unchanged
4667 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4668 networkctl_reload()
4669 self.wait_online(f'{link}:degraded')
4670 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4671 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
4672
4673 # expect retrans_time_ms unchanged
4674 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4675 networkctl_reload()
4676 self.wait_online(f'{link}:degraded')
4677 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4678 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
4679
4680 # expect retrans_time_ms unchanged
4681 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4682 networkctl_reload()
4683 self.wait_online(f'{link}:degraded')
4684 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
4685 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
4686
4687 # expect retrans_time_ms updated
4688 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4689 networkctl_reload()
4690 self.wait_online(f'{link}:degraded')
4691 self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000')
4692 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
4693
4694 def test_neighbor(self):
4695 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
4696 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
4697 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
4698 copy_dropins=False)
4699 start_networkd()
4700 self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
4701
4702 print('### ip neigh list dev gretun97')
4703 output = check_output('ip neigh list dev gretun97')
4704 print(output)
4705 self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
4706 self.assertNotIn('10.0.0.23', output)
4707
4708 print('### ip neigh list dev ip6gretun97')
4709 output = check_output('ip neigh list dev ip6gretun97')
4710 print(output)
4711 self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
4712 self.assertNotIn('2001:db8:0:f102::18', output)
4713
4714 print('### ip neigh list dev dummy98')
4715 output = check_output('ip neigh list dev dummy98')
4716 print(output)
4717 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
4718 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
4719 self.assertNotIn('2004:da8:1:0::2', output)
4720 self.assertNotIn('192.168.10.2', output)
4721 self.assertNotIn('00:00:5e:00:02:67', output)
4722
4723 check_json(networkctl_json())
4724
4725 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
4726 # the valid configurations in 10-step1.conf.
4727 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
4728 networkctl_reload()
4729 self.wait_online('dummy98:degraded')
4730
4731 print('### ip neigh list dev dummy98')
4732 output = check_output('ip neigh list dev dummy98')
4733 print(output)
4734 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
4735 self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
4736 self.assertNotIn('2004:da8:1:0::2', output)
4737 self.assertNotIn('192.168.10.2', output)
4738 self.assertNotIn('00:00:5e:00:02:67', output)
4739
4740 check_json(networkctl_json())
4741
4742 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
4743 '25-neighbor-dummy.network.d/10-step2.conf')
4744 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
4745 networkctl_reload()
4746 self.wait_online('dummy98:degraded')
4747
4748 print('### ip neigh list dev dummy98')
4749 output = check_output('ip neigh list dev dummy98')
4750 print(output)
4751 self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
4752 self.assertNotIn('00:00:5e:00:02:65', output)
4753 self.assertNotIn('00:00:5e:00:02:66', output)
4754 self.assertNotIn('00:00:5e:00:03:65', output)
4755 self.assertNotIn('2004:da8:1::1', output)
4756
4757 def test_link_local_addressing(self):
4758 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
4759 '25-link-local-addressing-no.network', '12-dummy.netdev')
4760 start_networkd()
4761 self.wait_online('test1:degraded', 'dummy98:carrier')
4762
4763 output = check_output('ip address show dev test1')
4764 print(output)
4765 self.assertRegex(output, 'inet .* scope link')
4766 self.assertRegex(output, 'inet6 .* scope link')
4767
4768 output = check_output('ip address show dev dummy98')
4769 print(output)
4770 self.assertNotRegex(output, 'inet6* .* scope link')
4771
4772 # Documentation/networking/ip-sysctl.txt
4773 #
4774 # addr_gen_mode - INTEGER
4775 # Defines how link-local and autoconf addresses are generated.
4776 #
4777 # 0: generate address based on EUI64 (default)
4778 # 1: do no generate a link-local address, use EUI64 for addresses generated
4779 # from autoconf
4780 # 2: generate stable privacy addresses, using the secret from
4781 # stable_secret (RFC7217)
4782 # 3: generate stable privacy addresses, using a random secret if unset
4783
4784 self.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
4785 self.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
4786 self.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
4787
4788 def test_link_local_addressing_ipv6ll(self):
4789 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
4790 start_networkd()
4791 self.wait_online('dummy98:degraded')
4792
4793 # An IPv6LL address exists by default.
4794 output = check_output('ip address show dev dummy98')
4795 print(output)
4796 self.assertRegex(output, 'inet6 .* scope link')
4797
4798 copy_network_unit('25-link-local-addressing-no.network')
4799 networkctl_reload()
4800 self.wait_online('dummy98:carrier')
4801
4802 # Check if the IPv6LL address is removed.
4803 output = check_output('ip address show dev dummy98')
4804 print(output)
4805 self.assertNotRegex(output, 'inet6 .* scope link')
4806
4807 remove_network_unit('25-link-local-addressing-no.network')
4808 networkctl_reload()
4809 self.wait_online('dummy98:degraded')
4810
4811 # Check if a new IPv6LL address is assigned.
4812 output = check_output('ip address show dev dummy98')
4813 print(output)
4814 self.assertRegex(output, 'inet6 .* scope link')
4815
4816 def test_sysctl(self):
4817 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
4818 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
4819 start_networkd()
4820 self.wait_online('dummy98:degraded')
4821
4822 self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
4823 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
4824 self.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
4825 self.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
4826 self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
4827 self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
4828 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
4829 self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
4830 self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
4831 self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
4832 self.check_ipv4_sysctl_attr('dummy98', 'force_igmp_version', '1')
4833
4834 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
4835 networkctl_reload()
4836 self.wait_online('dummy98:degraded')
4837
4838 self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
4839
4840 def test_sysctl_disable_ipv6(self):
4841 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
4842
4843 print('## Disable ipv6')
4844 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
4845 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
4846
4847 start_networkd()
4848 self.wait_online('dummy98:routable')
4849
4850 output = check_output('ip -4 address show dummy98')
4851 print(output)
4852 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
4853 output = check_output('ip -6 address show dummy98')
4854 print(output)
4855 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4856 self.assertRegex(output, 'inet6 .* scope link')
4857 output = check_output('ip -4 route show dev dummy98')
4858 print(output)
4859 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
4860 output = check_output('ip -6 route show default')
4861 print(output)
4862 self.assertRegex(output, 'default')
4863 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4864
4865 remove_link('dummy98')
4866
4867 print('## Enable ipv6')
4868 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
4869 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
4870
4871 restart_networkd()
4872 self.wait_online('dummy98:routable')
4873
4874 output = check_output('ip -4 address show dummy98')
4875 print(output)
4876 self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
4877 output = check_output('ip -6 address show dummy98')
4878 print(output)
4879 self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
4880 self.assertRegex(output, 'inet6 .* scope link')
4881 output = check_output('ip -4 route show dev dummy98')
4882 print(output)
4883 self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
4884 output = check_output('ip -6 route show default')
4885 print(output)
4886 self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
4887
4888 @expectedFailureIfModuleIsNotAvailable('mpls_router')
4889 def test_sysctl_mpls(self):
4890 check_output('modprobe mpls_router')
4891 copy_network_unit('25-sysctl-mpls.network', '12-dummy.netdev')
4892 start_networkd()
4893 self.wait_online('dummy98:degraded')
4894
4895 self.check_mpls_sysctl_attr('dummy98', 'input', '1')
4896
4897 def test_bind_carrier(self):
4898 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
4899 start_networkd()
4900
4901 # no bound interface.
4902 self.wait_operstate('test1', 'off', setup_state='configuring')
4903 output = check_output('ip address show test1')
4904 print(output)
4905 self.assertNotIn('UP,LOWER_UP', output)
4906 self.assertIn('DOWN', output)
4907 self.assertNotIn('192.168.10', output)
4908
4909 # add one bound interface. The interface will be up.
4910 check_output('ip link add dummy98 type dummy')
4911 check_output('ip link set dummy98 up')
4912 self.wait_online('test1:routable')
4913 output = check_output('ip address show test1')
4914 print(output)
4915 self.assertIn('UP,LOWER_UP', output)
4916 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4917
4918 # add another bound interface. The interface is still up.
4919 check_output('ip link add dummy99 type dummy')
4920 check_output('ip link set dummy99 up')
4921 self.wait_operstate('dummy99', 'degraded', setup_state='unmanaged')
4922 output = check_output('ip address show test1')
4923 print(output)
4924 self.assertIn('UP,LOWER_UP', output)
4925 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4926
4927 # remove one of the bound interfaces. The interface is still up
4928 remove_link('dummy98')
4929 output = check_output('ip address show test1')
4930 print(output)
4931 self.assertIn('UP,LOWER_UP', output)
4932 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4933
4934 # bring down the remaining bound interface. The interface will be down.
4935 check_output('ip link set dummy99 down')
4936 self.wait_operstate('test1', 'off')
4937 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4938 output = check_output('ip address show test1')
4939 print(output)
4940 self.assertNotIn('UP,LOWER_UP', output)
4941 self.assertIn('DOWN', output)
4942 self.assertNotIn('192.168.10', output)
4943
4944 # bring up the bound interface. The interface will be up.
4945 check_output('ip link set dummy99 up')
4946 self.wait_online('test1:routable')
4947 output = check_output('ip address show test1')
4948 print(output)
4949 self.assertIn('UP,LOWER_UP', output)
4950 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4951
4952 # remove the remaining bound interface. The interface will be down.
4953 remove_link('dummy99')
4954 self.wait_operstate('test1', 'off')
4955 self.wait_address_dropped('test1', r'192.168.10', ipv='-4', timeout_sec=10)
4956 output = check_output('ip address show test1')
4957 print(output)
4958 self.assertNotIn('UP,LOWER_UP', output)
4959 self.assertIn('DOWN', output)
4960 self.assertNotIn('192.168.10', output)
4961
4962 # re-add one bound interface. The interface will be up.
4963 check_output('ip link add dummy98 type dummy')
4964 check_output('ip link set dummy98 up')
4965 self.wait_online('test1:routable')
4966 output = check_output('ip address show test1')
4967 print(output)
4968 self.assertIn('UP,LOWER_UP', output)
4969 self.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output)
4970
4971 def _test_activation_policy(self, interface, test):
4972 conffile = '25-activation-policy.network'
4973 if test:
4974 conffile = f'{conffile}.d/{test}.conf'
4975 if interface == 'vlan99':
4976 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
4977 copy_network_unit('11-dummy.netdev', conffile, copy_dropins=False)
4978 start_networkd()
4979
4980 always = test.startswith('always')
4981 initial_up = test != 'manual' and not test.endswith('down') # note: default is up
4982 expect_up = initial_up
4983 next_up = not expect_up
4984
4985 if test.endswith('down'):
4986 self.wait_activated(interface)
4987
4988 for iteration in range(4):
4989 with self.subTest(iteration=iteration, expect_up=expect_up):
4990 operstate = 'routable' if expect_up else 'off'
4991 setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None)
4992 self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20)
4993
4994 if expect_up:
4995 self.assertIn('UP', check_output(f'ip link show {interface}'))
4996 self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}'))
4997 self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}'))
4998 else:
4999 self.assertIn('DOWN', check_output(f'ip link show {interface}'))
5000
5001 if next_up:
5002 check_output(f'ip link set dev {interface} up')
5003 else:
5004 check_output(f'ip link set dev {interface} down')
5005 expect_up = initial_up if always else next_up
5006 next_up = not next_up
5007 if always:
5008 time.sleep(1)
5009
5010 def test_activation_policy(self):
5011 first = True
5012 for interface in ['test1', 'vlan99']:
5013 for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
5014 if first:
5015 first = False
5016 else:
5017 self.tearDown()
5018
5019 print(f'### test_activation_policy(interface={interface}, test={test})')
5020 with self.subTest(interface=interface, test=test):
5021 self._test_activation_policy(interface, test)
5022
5023 def _test_activation_policy_required_for_online(self, policy, required):
5024 conffile = '25-activation-policy.network'
5025 units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
5026 if policy:
5027 units += [f'{conffile}.d/{policy}.conf']
5028 if required:
5029 units += [f'{conffile}.d/required-{required}.conf']
5030 copy_network_unit(*units, copy_dropins=False)
5031 start_networkd()
5032
5033 if policy.endswith('down'):
5034 self.wait_activated('test1')
5035
5036 if policy.endswith('down') or policy == 'manual':
5037 self.wait_operstate('test1', 'off', setup_state='configuring')
5038 else:
5039 self.wait_online('test1')
5040
5041 if policy == 'always-down':
5042 # if always-down, required for online is forced to no
5043 expected = False
5044 elif required:
5045 # otherwise if required for online is specified, it should match that
5046 expected = required == 'yes'
5047 elif policy:
5048 # otherwise if only policy specified, required for online defaults to
5049 # true if policy is up, always-up, or bound
5050 expected = policy.endswith('up') or policy == 'bound'
5051 else:
5052 # default is true, if neither are specified
5053 expected = True
5054
5055 output = networkctl_status('test1')
5056 print(output)
5057
5058 yesno = 'yes' if expected else 'no'
5059 self.assertRegex(output, f'Required For Online: {yesno}')
5060
5061 def test_activation_policy_required_for_online(self):
5062 first = True
5063 for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
5064 for required in ['yes', 'no', '']:
5065 if first:
5066 first = False
5067 else:
5068 self.tearDown()
5069
5070 print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})')
5071 with self.subTest(policy=policy, required=required):
5072 self._test_activation_policy_required_for_online(policy, required)
5073
5074 def test_domain(self):
5075 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
5076 start_networkd()
5077 self.wait_online('dummy98:routable')
5078
5079 output = networkctl_status('dummy98')
5080 print(output)
5081 self.assertRegex(output, 'Address: 192.168.42.100')
5082 self.assertRegex(output, 'DNS: 192.168.42.1')
5083 self.assertRegex(output, 'Search Domains: one')
5084
5085 def test_keep_configuration_yes(self):
5086 check_output('ip link add dummy98 type dummy')
5087 check_output('ip link set dev dummy98 up')
5088 check_output('ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98')
5089 check_output('ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot')
5090
5091 copy_network_unit('24-keep-configuration-yes.network')
5092 start_networkd()
5093 self.wait_online('dummy98:routable')
5094
5095 output = check_output('ip address show dummy98')
5096 print(output)
5097 self.assertIn('inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98', output)
5098
5099 output = check_output('ip -d -4 route show dev dummy98')
5100 print(output)
5101 self.assertIn('203.0.113.0/24 via 198.51.100.10 proto boot', output)
5102
5103 def test_keep_configuration_static(self):
5104 check_output('ip link add name dummy98 type dummy')
5105 check_output('ip address add 10.1.2.3/16 dev dummy98')
5106 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
5107 output = check_output('ip address show dummy98')
5108 print(output)
5109 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5110 self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5111 output = check_output('ip route show dev dummy98')
5112 print(output)
5113
5114 copy_network_unit('24-keep-configuration-static.network')
5115 start_networkd()
5116 self.wait_online('dummy98:routable')
5117
5118 output = check_output('ip address show dummy98')
5119 print(output)
5120 self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
5121 self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
5122
5123 def check_keep_configuration_on_restart(self):
5124 self.wait_online('dummy98:routable')
5125 self.wait_online('unmanaged0:routable', setup_state='unmanaged')
5126
5127 print('### ip -6 address show dev dummy98')
5128 output = check_output('ip -6 address show dev dummy98')
5129 print(output)
5130 self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
5131 self.assertIn('inet6 2001:db8:1:f101::15/64 scope global deprecated', output)
5132
5133 print('### ip -6 address show dev unmanaged0')
5134 output = check_output('ip -6 address show dev unmanaged0')
5135 print(output)
5136 self.assertIn('inet6 2001:db8:9999:f101::15/64 scope global', output)
5137
5138 print('### ip -6 route show default dev dummy98')
5139 output = check_output('ip -6 route show default dev dummy98')
5140 print(output)
5141 self.assertIn('default via fe80::f0ca:cc1a proto static metric 1 pref medium', output)
5142
5143 def test_keep_configuration_on_restart(self):
5144 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network', '85-unmanaged.link')
5145
5146 # Add an unmanaged interface with an up address
5147 call('ip link add unmanaged0 type dummy')
5148 call('ip link set unmanaged0 up')
5149 call('ip -4 addr add 10.20.30.40/32 dev unmanaged0')
5150 call('ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0')
5151
5152 # Wait for all addresses
5153 self.wait_address('unmanaged0', 'inet 10.20.30.40/32', scope='global', ipv='-4', timeout_sec=10)
5154 self.wait_address('unmanaged0', 'inet6 2001:db8:9999:f101::15/64', scope='global', ipv='-6', timeout_sec=10)
5155 self.wait_address('unmanaged0', 'inet6 fe80::[0-9a-f:]*/64', scope='link', ipv='-6', timeout_sec=10)
5156
5157 # Wait for all routes
5158 self.wait_route('unmanaged0', 'local 10.20.30.40 proto kernel', table='local', ipv='-4', timeout_sec=10)
5159 self.wait_route('unmanaged0', 'local fe80::[0-9a-f:]* proto kernel', table='local', ipv='-6', timeout_sec=10)
5160 self.wait_route('unmanaged0', 'multicast ff00::/8 proto kernel', table='local', ipv='-6', timeout_sec=10)
5161 self.wait_route('unmanaged0', '2001:db8:9999:f101::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5162 self.wait_route('unmanaged0', 'fe80::/64 proto kernel', table='main', ipv='-6', timeout_sec=10)
5163
5164 # Start `ip monitor` with output to a temporary file
5165 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor_u') as logfile_unmanaged:
5166 process_u = subprocess.Popen(['ip', 'monitor', 'dev', 'unmanaged0'], stdout=logfile_unmanaged, text=True)
5167
5168 start_networkd()
5169 self.check_keep_configuration_on_restart()
5170
5171 # Start `ip monitor` with output to a temporary file
5172 with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor') as logfile:
5173 process = subprocess.Popen(['ip', 'monitor', 'dev', 'dummy98'], stdout=logfile, text=True)
5174
5175 restart_networkd()
5176 self.check_keep_configuration_on_restart()
5177
5178 process.send_signal(signal.SIGTERM)
5179 process.wait()
5180
5181 print('### ip monitor dev dummy98 BEGIN')
5182
5183 # Read the `ip monitor` output looking for network changes
5184 logfile.seek(0)
5185 for line in logfile:
5186 line = line.rstrip()
5187 print(line)
5188 # Check if a link went down
5189 self.assertNotRegex(line, 'dummy98: .* state DOWN')
5190 # Check if an address was removed
5191 self.assertNotRegex(line, '^Deleted .* 2001:db8:')
5192 self.assertNotRegex(line, '^Deleted 2001:db8:.*/64')
5193 # Check if the default route was removed
5194 self.assertNotRegex(line, '^Deleted default via fe80::f0ca:cc1a')
5195
5196 print('### ip monitor dev dummy98 END')
5197
5198 process_u.send_signal(signal.SIGTERM)
5199 process_u.wait()
5200
5201 print('### ip monitor dev unmanaged0 BEGIN')
5202
5203 # Read the `ip monitor` output looking for network changes and check if something happened
5204 logfile_unmanaged.seek(0)
5205 self.assertEqual(logfile_unmanaged.read().rstrip(), '')
5206
5207 print('### ip monitor dev unmanaged0 END')
5208
5209 def test_keep_untracked_addresses(self):
5210 # Add an unmanaged interface with an up address
5211
5212 copy_network_unit('12-dummy.netdev', '85-static-ipv6.network')
5213 start_networkd()
5214 self.wait_online('dummy98:routable')
5215
5216 print('### ip -4 addr add 10.234.77.111/32 dev dummy98')
5217 output = check_output('ip -4 addr add 10.234.77.111/32 dev dummy98')
5218 print(output)
5219
5220 print('### ip -6 addr add 2222:3333::4444/64 dev dummy98')
5221 output = check_output('ip -6 addr add 2222:3333::4444/64 dev dummy98')
5222 print(output)
5223
5224 restart_networkd()
5225
5226 output = check_output('ip -4 addr show dev dummy98')
5227 print(output)
5228 self.assertIn('inet 10.234.77.111/32', output)
5229
5230 output = check_output('ip -6 addr show dev dummy98')
5231 print(output)
5232 self.assertIn('inet6 2222:3333::4444/64 scope global', output)
5233
5234 def check_nexthop(self, manage_foreign_nexthops, first):
5235 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
5236
5237 output = check_output('ip nexthop list dev veth99')
5238 print(output)
5239 if first:
5240 self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
5241 self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
5242 else:
5243 self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
5244 self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output)
5245 self.assertIn('id 3 dev veth99', output)
5246 self.assertIn('id 4 dev veth99', output)
5247 if first:
5248 self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
5249 else:
5250 self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
5251 self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
5252 self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
5253 if manage_foreign_nexthops:
5254 self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
5255
5256 output = check_output('ip nexthop list dev dummy98')
5257 print(output)
5258 if first:
5259 self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
5260 else:
5261 self.assertIn('id 21 via 192.168.20.1 dev dummy98', output)
5262 if manage_foreign_nexthops:
5263 self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
5264 else:
5265 self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
5266
5267 # kernel manages blackhole nexthops on lo
5268 output = check_output('ip nexthop list dev lo')
5269 print(output)
5270 if first:
5271 self.assertIn('id 6 blackhole', output)
5272 self.assertIn('id 7 blackhole', output)
5273 else:
5274 self.assertIn('id 1 blackhole', output)
5275 self.assertIn('id 2 blackhole', output)
5276
5277 # group nexthops are shown with -0 option
5278 if first:
5279 output = check_output('ip -0 nexthop list id 21')
5280 print(output)
5281 self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
5282 else:
5283 output = check_output('ip -0 nexthop list id 20')
5284 print(output)
5285 self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)')
5286
5287 output = check_output('ip route show dev veth99 10.10.10.10')
5288 print(output)
5289 if first:
5290 self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
5291 else:
5292 self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output)
5293
5294 output = check_output('ip route show dev veth99 10.10.10.11')
5295 print(output)
5296 if first:
5297 self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
5298 else:
5299 self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output)
5300
5301 output = check_output('ip route show dev veth99 10.10.10.12')
5302 print(output)
5303 if first:
5304 self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
5305 else:
5306 self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output)
5307
5308 output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
5309 print(output)
5310 if first:
5311 self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
5312 else:
5313 self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
5314
5315 output = check_output('ip route show 10.10.10.13')
5316 print(output)
5317 if first:
5318 self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
5319 else:
5320 self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output)
5321
5322 output = check_output('ip -6 route show 2001:1234:5:8f62::2')
5323 print(output)
5324 if first:
5325 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
5326 else:
5327 self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output)
5328
5329 output = check_output('ip route show 10.10.10.14')
5330 print(output)
5331 if first:
5332 self.assertIn('10.10.10.14 nhid 21 proto static', output)
5333 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5334 else:
5335 self.assertIn('10.10.10.14 nhid 20 proto static', output)
5336 self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output)
5337 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
5338
5339 output = networkctl_json()
5340 check_json(output)
5341 self.assertNotIn('"Destination":[10.10.10.14]', output)
5342
5343 def _test_nexthop(self, manage_foreign_nexthops):
5344 if not manage_foreign_nexthops:
5345 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
5346
5347 check_output('ip link add dummy98 type dummy')
5348 check_output('ip link set dummy98 up')
5349 check_output('ip address add 192.168.20.20/24 dev dummy98')
5350 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
5351
5352 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
5353 '12-dummy.netdev', '25-nexthop-dummy-1.network')
5354 start_networkd()
5355
5356 self.check_nexthop(manage_foreign_nexthops, first=True)
5357
5358 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
5359 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
5360 networkctl_reload()
5361 self.check_nexthop(manage_foreign_nexthops, first=False)
5362
5363 remove_network_unit('25-nexthop-2.network')
5364 copy_network_unit('25-nexthop-nothing.network')
5365 networkctl_reload()
5366 self.wait_online('veth99:routable', 'veth-peer:routable')
5367
5368 output = check_output('ip nexthop list dev veth99')
5369 print(output)
5370 self.assertEqual(output, '')
5371 output = check_output('ip nexthop list dev lo')
5372 print(output)
5373 self.assertEqual(output, '')
5374
5375 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
5376 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
5377 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
5378 # here to test reconfiguring with different .network files does not trigger race.
5379 # See also comments in link_drop_requests().
5380 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
5381 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
5382
5383 self.check_nexthop(manage_foreign_nexthops, first=True)
5384
5385 # Remove nexthop with ID 20
5386 check_output('ip nexthop del id 20')
5387
5388 # Check the nexthop ID 20 is dropped from the group nexthop.
5389 output = check_output('ip -0 nexthop list id 21')
5390 print(output)
5391 self.assertRegex(output, r'id 21 group 1,3')
5392
5393 # Remove nexthop with ID 21
5394 check_output('ip nexthop del id 21')
5395 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
5396 networkctl_reload()
5397
5398 # 25-nexthop-test1.network requests a route with nexthop ID 21, which is removed in the above,
5399 # hence test1 should be stuck in the configuring state.
5400 self.wait_operstate('test1', operstate='routable', setup_state='configuring')
5401
5402 # Wait for a while, and check if the interface is still in the configuring state.
5403 time.sleep(1)
5404 output = networkctl_status('test1')
5405 self.assertIn('State: routable (configuring)', output)
5406
5407 # Check if the route which needs nexthop 21 are forgotten.
5408 output = networkctl_json()
5409 check_json(output)
5410 self.assertNotIn('"Destination":[10.10.10.14]', output)
5411
5412 # Reconfigure the interface that has nexthop with ID 20 and 21,
5413 # then the route requested by test1 can be configured.
5414 networkctl_reconfigure('dummy98')
5415 self.wait_online('test1:routable')
5416
5417 # Check if the requested route actually configured.
5418 output = check_output('ip route show 10.10.11.10')
5419 print(output)
5420 self.assertIn('10.10.11.10 nhid 21 proto static', output)
5421 self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
5422 self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
5423
5424 remove_link('veth99')
5425 time.sleep(2)
5426
5427 output = check_output('ip nexthop list dev lo')
5428 print(output)
5429 self.assertEqual(output, '')
5430
5431 @expectedFailureIfNexthopIsNotAvailable()
5432 def test_nexthop(self):
5433 first = True
5434 for manage_foreign_nexthops in [True, False]:
5435 if first:
5436 first = False
5437 else:
5438 self.tearDown()
5439
5440 print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
5441 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
5442 self._test_nexthop(manage_foreign_nexthops)
5443
5444 class NetworkdTCTests(unittest.TestCase, Utilities):
5445
5446 def setUp(self):
5447 setup_common()
5448
5449 def tearDown(self):
5450 tear_down_common()
5451
5452 @expectedFailureIfModuleIsNotAvailable('sch_cake')
5453 def test_qdisc_cake(self):
5454 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
5455 start_networkd()
5456 self.wait_online('dummy98:routable')
5457
5458 output = check_output('tc qdisc show dev dummy98')
5459 print(output)
5460 self.assertIn('qdisc cake 3a: root', output)
5461 self.assertIn('bandwidth 500Mbit', output)
5462 self.assertIn('autorate-ingress', output)
5463 self.assertIn('diffserv8', output)
5464 self.assertIn('dual-dsthost', output)
5465 self.assertIn(' nat', output)
5466 self.assertIn(' wash', output)
5467 self.assertIn(' split-gso', output)
5468 self.assertIn(' raw', output)
5469 self.assertIn(' atm', output)
5470 self.assertIn('overhead 128', output)
5471 self.assertIn('mpu 20', output)
5472 self.assertIn('fwmark 0xff00', output)
5473 self.assertIn('rtt 1s', output)
5474 self.assertIn('ack-filter-aggressive', output)
5475
5476 # Test for replacing existing qdisc. See #31226.
5477 with open(os.path.join(network_unit_dir, '25-qdisc-cake.network'), mode='a', encoding='utf-8') as f:
5478 f.write('Bandwidth=250M\n')
5479
5480 networkctl_reload()
5481 self.wait_online('dummy98:routable')
5482
5483 output = check_output('tc qdisc show dev dummy98')
5484 print(output)
5485 self.assertIn('bandwidth 250Mbit', output)
5486
5487 @expectedFailureIfModuleIsNotAvailable('sch_codel')
5488 def test_qdisc_codel(self):
5489 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
5490 start_networkd()
5491 self.wait_online('dummy98:routable')
5492
5493 output = check_output('tc qdisc show dev dummy98')
5494 print(output)
5495 self.assertRegex(output, 'qdisc codel 33: root')
5496 self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
5497
5498 @expectedFailureIfModuleIsNotAvailable('sch_drr')
5499 def test_qdisc_drr(self):
5500 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
5501 start_networkd()
5502 self.wait_online('dummy98:routable')
5503
5504 output = check_output('tc qdisc show dev dummy98')
5505 print(output)
5506 self.assertRegex(output, 'qdisc drr 2: root')
5507 output = check_output('tc class show dev dummy98')
5508 print(output)
5509 self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
5510
5511 @expectedFailureIfModuleIsNotAvailable('sch_ets')
5512 def test_qdisc_ets(self):
5513 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
5514 start_networkd()
5515 self.wait_online('dummy98:routable')
5516
5517 output = check_output('tc qdisc show dev dummy98')
5518 print(output)
5519
5520 self.assertRegex(output, 'qdisc ets 3a: root')
5521 self.assertRegex(output, 'bands 10 strict 3')
5522 self.assertRegex(output, 'quanta 1 2 3 4 5')
5523 self.assertRegex(output, 'priomap 3 4 5 6 7')
5524
5525 @expectedFailureIfModuleIsNotAvailable('sch_fq')
5526 def test_qdisc_fq(self):
5527 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
5528 start_networkd()
5529 self.wait_online('dummy98:routable')
5530
5531 output = check_output('tc qdisc show dev dummy98')
5532 print(output)
5533 self.assertRegex(output, 'qdisc fq 32: root')
5534 self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
5535 self.assertRegex(output, 'quantum 1500')
5536 self.assertRegex(output, 'initial_quantum 13000')
5537 self.assertRegex(output, 'maxrate 1Mbit')
5538
5539 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
5540 def test_qdisc_fq_codel(self):
5541 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
5542 start_networkd()
5543 self.wait_online('dummy98:routable')
5544
5545 output = check_output('tc qdisc show dev dummy98')
5546 print(output)
5547 self.assertRegex(output, 'qdisc fq_codel 34: root')
5548 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')
5549
5550 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
5551 def test_qdisc_fq_pie(self):
5552 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
5553 start_networkd()
5554 self.wait_online('dummy98:routable')
5555
5556 output = check_output('tc qdisc show dev dummy98')
5557 print(output)
5558
5559 self.assertRegex(output, 'qdisc fq_pie 3a: root')
5560 self.assertRegex(output, 'limit 200000p')
5561
5562 @expectedFailureIfModuleIsNotAvailable('sch_gred')
5563 def test_qdisc_gred(self):
5564 copy_network_unit('25-qdisc-gred.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 self.assertRegex(output, 'qdisc gred 38: root')
5571 self.assertRegex(output, 'vqs 12 default 10 grio')
5572
5573 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
5574 def test_qdisc_hhf(self):
5575 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
5576 start_networkd()
5577 self.wait_online('dummy98:routable')
5578
5579 output = check_output('tc qdisc show dev dummy98')
5580 print(output)
5581 self.assertRegex(output, 'qdisc hhf 3a: root')
5582 self.assertRegex(output, 'limit 1022p')
5583
5584 @expectedFailureIfModuleIsNotAvailable('sch_htb')
5585 def test_qdisc_htb_fifo(self):
5586 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
5587 start_networkd()
5588 self.wait_online('dummy98:routable')
5589
5590 output = check_output('tc qdisc show dev dummy98')
5591 print(output)
5592 self.assertRegex(output, 'qdisc htb 2: root')
5593 self.assertRegex(output, r'default (0x30|30)')
5594
5595 self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
5596 self.assertRegex(output, 'limit 100000p')
5597
5598 self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
5599 self.assertRegex(output, 'limit 1000000')
5600
5601 self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
5602 self.assertRegex(output, 'limit 1023p')
5603
5604 self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
5605
5606 output = check_output('tc -d class show dev dummy98')
5607 print(output)
5608 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
5609 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
5610 # which is fixed in v6.3.0 by
5611 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
5612 self.assertRegex(output, 'class htb 2:37 root leaf 37(:|prio) ')
5613 self.assertRegex(output, 'class htb 2:3a root leaf 3a(:|prio) ')
5614 self.assertRegex(output, 'class htb 2:3b root leaf 3b(:|prio) ')
5615 self.assertRegex(output, 'class htb 2:3c root leaf 3c(:|prio) ')
5616 self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
5617 self.assertRegex(output, 'burst 123456')
5618 self.assertRegex(output, 'cburst 123457')
5619
5620 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
5621 def test_qdisc_ingress(self):
5622 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
5623 '25-qdisc-ingress.network', '11-dummy.netdev')
5624 start_networkd()
5625 self.wait_online('dummy98:routable', 'test1:routable')
5626
5627 output = check_output('tc qdisc show dev dummy98')
5628 print(output)
5629 self.assertRegex(output, 'qdisc clsact')
5630
5631 output = check_output('tc qdisc show dev test1')
5632 print(output)
5633 self.assertRegex(output, 'qdisc ingress')
5634
5635 def test_qdisc_mq(self):
5636 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-mq.network')
5637 start_networkd()
5638 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5639
5640 output = check_output('tc qdisc show dev testtun99')
5641 print(output)
5642 self.assertIn('qdisc mq 2: root', output)
5643
5644 @expectedFailureIfModuleIsNotAvailable('sch_multiq')
5645 def test_qdisc_multiq(self):
5646 copy_network_unit('25-tun.netdev', '25-tap.netdev', '25-qdisc-multiq.network')
5647 start_networkd()
5648 self.wait_online('testtun99:degraded', 'testtap99:degraded')
5649
5650 output = check_output('tc qdisc show dev testtun99')
5651 print(output)
5652 self.assertIn('qdisc multiq 2: root', output)
5653
5654 @expectedFailureIfModuleIsNotAvailable('sch_netem')
5655 def test_qdisc_netem(self):
5656 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
5657 '25-qdisc-netem-compat.network', '11-dummy.netdev')
5658 start_networkd()
5659 self.wait_online('dummy98:routable', 'test1:routable')
5660
5661 output = check_output('tc qdisc show dev dummy98')
5662 print(output)
5663 self.assertRegex(output, 'qdisc netem 30: root')
5664 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
5665
5666 output = check_output('tc qdisc show dev test1')
5667 print(output)
5668 self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
5669 self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
5670
5671 @expectedFailureIfModuleIsNotAvailable('sch_pie')
5672 def test_qdisc_pie(self):
5673 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
5674 start_networkd()
5675 self.wait_online('dummy98:routable')
5676
5677 output = check_output('tc qdisc show dev dummy98')
5678 print(output)
5679 self.assertRegex(output, 'qdisc pie 3a: root')
5680 self.assertRegex(output, 'limit 200000')
5681
5682 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
5683 def test_qdisc_qfq(self):
5684 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
5685 start_networkd()
5686 self.wait_online('dummy98:routable')
5687
5688 output = check_output('tc qdisc show dev dummy98')
5689 print(output)
5690 self.assertRegex(output, 'qdisc qfq 2: root')
5691 output = check_output('tc class show dev dummy98')
5692 print(output)
5693 self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
5694 self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
5695
5696 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
5697 def test_qdisc_sfb(self):
5698 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
5699 start_networkd()
5700 self.wait_online('dummy98:routable')
5701
5702 output = check_output('tc qdisc show dev dummy98')
5703 print(output)
5704 self.assertRegex(output, 'qdisc sfb 39: root')
5705 self.assertRegex(output, 'limit 200000')
5706
5707 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
5708 def test_qdisc_sfq(self):
5709 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
5710 start_networkd()
5711 self.wait_online('dummy98:routable')
5712
5713 output = check_output('tc qdisc show dev dummy98')
5714 print(output)
5715 self.assertRegex(output, 'qdisc sfq 36: root')
5716 self.assertRegex(output, 'perturb 5sec')
5717
5718 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
5719 def test_qdisc_tbf(self):
5720 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
5721 start_networkd()
5722 self.wait_online('dummy98:routable')
5723
5724 output = check_output('tc qdisc show dev dummy98')
5725 print(output)
5726 self.assertRegex(output, 'qdisc tbf 35: root')
5727 self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms')
5728
5729 @expectedFailureIfModuleIsNotAvailable('sch_teql')
5730 def test_qdisc_teql(self):
5731 call_quiet('rmmod sch_teql')
5732
5733 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
5734 start_networkd()
5735 self.wait_links('dummy98')
5736 check_output('modprobe sch_teql max_equalizers=2')
5737 self.wait_online('dummy98:routable')
5738
5739 output = check_output('tc qdisc show dev dummy98')
5740 print(output)
5741 self.assertRegex(output, 'qdisc teql1 31: root')
5742
5743 @expectedFailureIfModuleIsNotAvailable('sch_fq', 'sch_sfq', 'sch_tbf')
5744 def test_qdisc_drop(self):
5745 copy_network_unit('12-dummy.netdev', '12-dummy.network')
5746 start_networkd()
5747 self.wait_online('dummy98:routable')
5748
5749 # Test case for issue #32247 and #32254.
5750 for _ in range(20):
5751 check_output('tc qdisc replace dev dummy98 root fq')
5752 self.assertFalse(networkd_is_failed())
5753 check_output('tc qdisc replace dev dummy98 root fq pacing')
5754 self.assertFalse(networkd_is_failed())
5755 check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540')
5756 self.assertFalse(networkd_is_failed())
5757 check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq')
5758 self.assertFalse(networkd_is_failed())
5759
5760 class NetworkdStateFileTests(unittest.TestCase, Utilities):
5761
5762 def setUp(self):
5763 setup_common()
5764
5765 def tearDown(self):
5766 tear_down_common()
5767
5768 def test_state_file(self):
5769 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
5770 start_networkd()
5771 self.wait_online('dummy98:routable')
5772
5773 # make link state file updated
5774 resolvectl('revert', 'dummy98')
5775
5776 check_json(networkctl_json())
5777
5778 output = read_link_state_file('dummy98')
5779 print(output)
5780 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5781 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5782 self.assertIn('ADMIN_STATE=configured', output)
5783 self.assertIn('OPER_STATE=routable', output)
5784 self.assertIn('REQUIRED_FOR_ONLINE=yes', output)
5785 self.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output)
5786 self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output)
5787 self.assertIn('ACTIVATION_POLICY=up', output)
5788 self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output)
5789 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5790 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5791 self.assertIn('DOMAINS=hogehoge', output)
5792 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5793 self.assertIn('LLMNR=no', output)
5794 self.assertIn('MDNS=yes', output)
5795 self.assertIn('DNSSEC=no', output)
5796
5797 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
5798 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
5799 resolvectl('llmnr', 'dummy98', 'yes')
5800 resolvectl('mdns', 'dummy98', 'no')
5801 resolvectl('dnssec', 'dummy98', 'yes')
5802 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
5803
5804 check_json(networkctl_json())
5805
5806 output = read_link_state_file('dummy98')
5807 print(output)
5808 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5809 self.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output)
5810 self.assertIn('DOMAINS=hogehogehoge', output)
5811 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5812 self.assertIn('LLMNR=yes', output)
5813 self.assertIn('MDNS=no', output)
5814 self.assertIn('DNSSEC=yes', output)
5815
5816 timedatectl('revert', 'dummy98')
5817
5818 check_json(networkctl_json())
5819
5820 output = read_link_state_file('dummy98')
5821 print(output)
5822 self.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output)
5823 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5824 self.assertIn('DOMAINS=hogehogehoge', output)
5825 self.assertIn('ROUTE_DOMAINS=foofoofoo', output)
5826 self.assertIn('LLMNR=yes', output)
5827 self.assertIn('MDNS=no', output)
5828 self.assertIn('DNSSEC=yes', output)
5829
5830 resolvectl('revert', 'dummy98')
5831
5832 check_json(networkctl_json())
5833
5834 output = read_link_state_file('dummy98')
5835 print(output)
5836 self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output)
5837 self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output)
5838 self.assertIn('DOMAINS=hogehoge', output)
5839 self.assertIn('ROUTE_DOMAINS=foofoo', output)
5840 self.assertIn('LLMNR=no', output)
5841 self.assertIn('MDNS=yes', output)
5842 self.assertIn('DNSSEC=no', output)
5843
5844 def test_address_state(self):
5845 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
5846 start_networkd()
5847
5848 self.wait_online('dummy98:degraded')
5849
5850 output = read_link_state_file('dummy98')
5851 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5852 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5853
5854 # with a routable IPv4 address
5855 check_output('ip address add 10.1.2.3/16 dev dummy98')
5856 self.wait_online('dummy98:routable', ipv4=True)
5857 self.wait_online('dummy98:routable')
5858
5859 output = read_link_state_file('dummy98')
5860 self.assertIn('IPV4_ADDRESS_STATE=routable', output)
5861 self.assertIn('IPV6_ADDRESS_STATE=degraded', output)
5862
5863 check_output('ip address del 10.1.2.3/16 dev dummy98')
5864
5865 # with a routable IPv6 address
5866 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
5867 self.wait_online('dummy98:routable', ipv6=True)
5868 self.wait_online('dummy98:routable')
5869
5870 output = read_link_state_file('dummy98')
5871 self.assertIn('IPV4_ADDRESS_STATE=off', output)
5872 self.assertIn('IPV6_ADDRESS_STATE=routable', output)
5873
5874 class NetworkdBondTests(unittest.TestCase, Utilities):
5875
5876 def setUp(self):
5877 setup_common()
5878
5879 def tearDown(self):
5880 tear_down_common()
5881
5882 def test_bond_keep_master(self):
5883 check_output('ip link add bond199 type bond mode active-backup')
5884 check_output('ip link add dummy98 type dummy')
5885 check_output('ip link set dummy98 master bond199')
5886
5887 copy_network_unit('23-keep-master.network')
5888 start_networkd()
5889 self.wait_online('dummy98:enslaved')
5890
5891 output = check_output('ip -d link show bond199')
5892 print(output)
5893 self.assertRegex(output, 'active_slave dummy98')
5894
5895 output = check_output('ip -d link show dummy98')
5896 print(output)
5897 self.assertRegex(output, 'master bond199')
5898
5899 # Test case for #37629
5900 for _ in range(3):
5901 # When a slave leaved from its master bonding interface, the kernel brings down the slave.
5902 check_output('ip link set dummy98 nomaster')
5903 self.wait_online('dummy98:off')
5904
5905 # Bring up the interface to check if networkd recognizes the interface has no master now.
5906 check_output('ip link set dummy98 up')
5907 self.wait_online('dummy98:carrier')
5908
5909 # We need to first bring down the interface to make it join a bonding interface.
5910 check_output('ip link set dummy98 down')
5911 check_output('ip link set dummy98 master bond199')
5912 self.wait_online('dummy98:enslaved')
5913
5914 def test_bond_active_slave(self):
5915 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
5916 start_networkd()
5917 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5918
5919 output = check_output('ip -d link show bond199')
5920 print(output)
5921 self.assertIn('active_slave dummy98', output)
5922
5923 # test case for issue #31165.
5924 since = datetime.datetime.now()
5925 networkctl_reconfigure('dummy98')
5926 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5927 self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
5928
5929 # test for reloading.
5930 touch_network_unit(
5931 '23-active-slave.network',
5932 '23-bond199.network',
5933 '25-bond-active-backup-slave.netdev',
5934 '12-dummy.netdev')
5935 networkctl_reload()
5936 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5937
5938 def test_bond_primary_slave(self):
5939 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
5940 start_networkd()
5941 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5942
5943 output = check_output('ip -d link show bond199')
5944 print(output)
5945 self.assertIn('primary dummy98', output)
5946
5947 # for issue #25627
5948 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
5949 for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
5950 with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
5951 f.write(f'[Link]\nMACAddress={mac}\n')
5952
5953 networkctl_reload()
5954 self.wait_online('dummy98:enslaved', 'bond199:degraded')
5955
5956 output = check_output('ip -d link show bond199')
5957 print(output)
5958 self.assertIn(f'link/ether {mac}', output)
5959
5960 def test_bond_operstate(self):
5961 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
5962 '25-bond99.network', '25-bond-slave.network')
5963 start_networkd()
5964 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
5965
5966 output = check_output('ip -d link show dummy98')
5967 print(output)
5968 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
5969
5970 output = check_output('ip -d link show test1')
5971 print(output)
5972 self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
5973
5974 output = check_output('ip -d link show bond99')
5975 print(output)
5976 self.assertRegex(output, 'MASTER,UP,LOWER_UP')
5977
5978 # test case for issue #32186
5979 restart_networkd()
5980 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
5981
5982 self.wait_operstate('dummy98', 'enslaved')
5983 self.wait_operstate('test1', 'enslaved')
5984 self.wait_operstate('bond99', 'routable')
5985
5986 check_output('ip link set dummy98 down')
5987
5988 self.wait_operstate('dummy98', 'off')
5989 self.wait_operstate('test1', 'enslaved')
5990 self.wait_operstate('bond99', 'routable')
5991
5992 check_output('ip link set dummy98 up')
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 check_output('ip link set test1 down')
6000
6001 self.wait_operstate('dummy98', 'off')
6002 self.wait_operstate('test1', 'off')
6003
6004 if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
6005 # Huh? Kernel does not recognize that all slave interfaces are down?
6006 # Let's confirm that networkd's operstate is consistent with ip's result.
6007 output = check_output('ip -d link show bond99')
6008 print(output)
6009 self.assertNotRegex(output, 'NO-CARRIER')
6010
6011 class NetworkdBridgeTests(unittest.TestCase, Utilities):
6012
6013 def setUp(self):
6014 setup_common()
6015
6016 def tearDown(self):
6017 tear_down_common()
6018
6019 def test_bridge_mac_none(self):
6020 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
6021 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
6022 start_networkd()
6023 self.wait_online('dummy98:enslaved', 'bridge99:degraded')
6024
6025 output = check_output('ip link show dev dummy98')
6026 print(output)
6027 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6028
6029 output = check_output('ip link show dev bridge99')
6030 print(output)
6031 self.assertIn('link/ether 12:34:56:78:9a:01', output)
6032
6033 def test_bridge_vlan(self):
6034 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
6035 '26-bridge.netdev', '26-bridge-vlan-master.network',
6036 copy_dropins=False)
6037 start_networkd()
6038 self.wait_online('test1:enslaved', 'bridge99:degraded')
6039
6040 output = check_output('bridge vlan show dev test1')
6041 print(output)
6042 # check if the default VID is removed
6043 self.assertNotIn('1 Egress Untagged', output)
6044 for i in range(1000, 3000):
6045 if i == 1010:
6046 self.assertIn(f'{i} PVID', output)
6047 elif i in range(1012, 1016) or i in range(1103, 1109):
6048 self.assertIn(f'{i} Egress Untagged', output)
6049 elif i in range(1008, 1014) or i in range(1100, 1111):
6050 self.assertIn(f'{i}', output)
6051 else:
6052 self.assertNotIn(f'{i}', output)
6053
6054 output = check_output('bridge vlan show dev bridge99')
6055 print(output)
6056 # check if the default VID is removed
6057 self.assertNotIn('1 Egress Untagged', output)
6058 for i in range(1000, 3000):
6059 if i == 1020:
6060 self.assertIn(f'{i} PVID', output)
6061 elif i in range(1022, 1026) or i in range(1203, 1209):
6062 self.assertIn(f'{i} Egress Untagged', output)
6063 elif i in range(1018, 1024) or i in range(1200, 1211):
6064 self.assertIn(f'{i}', output)
6065 else:
6066 self.assertNotIn(f'{i}', output)
6067
6068 # Change vlan IDs
6069 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
6070 '26-bridge-vlan-master.network.d/10-override.conf')
6071 networkctl_reload()
6072 self.wait_online('test1:enslaved', 'bridge99:degraded')
6073
6074 output = check_output('bridge vlan show dev test1')
6075 print(output)
6076 for i in range(1000, 3000):
6077 if i == 2010:
6078 self.assertIn(f'{i} PVID', output)
6079 elif i in range(2012, 2016) or i in range(2103, 2109):
6080 self.assertIn(f'{i} Egress Untagged', output)
6081 elif i in range(2008, 2014) or i in range(2100, 2111):
6082 self.assertIn(f'{i}', output)
6083 else:
6084 self.assertNotIn(f'{i}', output)
6085
6086 output = check_output('bridge vlan show dev bridge99')
6087 print(output)
6088 for i in range(1000, 3000):
6089 if i == 2020:
6090 self.assertIn(f'{i} PVID', output)
6091 elif i in range(2022, 2026) or i in range(2203, 2209):
6092 self.assertIn(f'{i} Egress Untagged', output)
6093 elif i in range(2018, 2024) or i in range(2200, 2211):
6094 self.assertIn(f'{i}', output)
6095 else:
6096 self.assertNotIn(f'{i}', output)
6097
6098 # Remove several vlan IDs
6099 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
6100 '26-bridge-vlan-master.network.d/20-override.conf')
6101 networkctl_reload()
6102 self.wait_online('test1:enslaved', 'bridge99:degraded')
6103
6104 output = check_output('bridge vlan show dev test1')
6105 print(output)
6106 for i in range(1000, 3000):
6107 if i == 2010:
6108 self.assertIn(f'{i} PVID', output)
6109 elif i in range(2012, 2016):
6110 self.assertIn(f'{i} Egress Untagged', output)
6111 elif i in range(2008, 2014):
6112 self.assertIn(f'{i}', output)
6113 else:
6114 self.assertNotIn(f'{i}', output)
6115
6116 output = check_output('bridge vlan show dev bridge99')
6117 print(output)
6118 for i in range(1000, 3000):
6119 if i == 2020:
6120 self.assertIn(f'{i} PVID', output)
6121 elif i in range(2022, 2026):
6122 self.assertIn(f'{i} Egress Untagged', output)
6123 elif i in range(2018, 2024):
6124 self.assertIn(f'{i}', output)
6125 else:
6126 self.assertNotIn(f'{i}', output)
6127
6128 # Remove all vlan IDs
6129 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
6130 '26-bridge-vlan-master.network.d/30-override.conf')
6131 networkctl_reload()
6132 self.wait_online('test1:enslaved', 'bridge99:degraded')
6133
6134 output = check_output('bridge vlan show dev test1')
6135 print(output)
6136 self.assertNotIn('PVID', output)
6137 for i in range(1000, 3000):
6138 self.assertNotIn(f'{i}', output)
6139
6140 output = check_output('bridge vlan show dev bridge99')
6141 print(output)
6142 self.assertNotIn('PVID', output)
6143 for i in range(1000, 3000):
6144 self.assertNotIn(f'{i}', output)
6145
6146 def test_bridge_vlan_issue_20373(self):
6147 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
6148 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
6149 '21-vlan.netdev', '21-vlan.network')
6150 start_networkd()
6151 self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
6152
6153 output = check_output('bridge vlan show dev test1')
6154 print(output)
6155 self.assertIn('100 PVID Egress Untagged', output)
6156 self.assertIn('560', output)
6157 self.assertIn('600', output)
6158
6159 output = check_output('bridge vlan show dev bridge99')
6160 print(output)
6161 self.assertIn('1 PVID Egress Untagged', output)
6162 self.assertIn('100', output)
6163 self.assertIn('600', output)
6164
6165 def test_bridge_mdb(self):
6166 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
6167 '26-bridge.netdev', '26-bridge-mdb-master.network')
6168 start_networkd()
6169 self.wait_online('test1:enslaved', 'bridge99:degraded')
6170
6171 output = check_output('bridge mdb show dev bridge99')
6172 print(output)
6173 self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
6174 self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
6175
6176 # Old kernel may not support bridge MDB entries on bridge master
6177 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
6178 self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
6179 self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
6180
6181 # Old kernel may not support L2 bridge MDB entries
6182 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0:
6183 self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069')
6184
6185 def test_bridge_keep_master(self):
6186 check_output('ip link add bridge99 type bridge')
6187 check_output('ip link set bridge99 up')
6188 check_output('ip link add dummy98 type dummy')
6189 check_output('ip link set dummy98 master bridge99')
6190
6191 copy_network_unit('23-keep-master.network')
6192 start_networkd()
6193 self.wait_online('dummy98:enslaved')
6194
6195 output = check_output('ip -d link show dummy98')
6196 print(output)
6197 self.assertRegex(output, 'master bridge99')
6198 self.assertRegex(output, 'bridge')
6199
6200 output = check_output('bridge -d link show dummy98')
6201 print(output)
6202 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6203 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6204 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6205 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6206 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
6207 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6208 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6209 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6210 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6211 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
6212 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6213 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
6214
6215 def check_bridge_property(self):
6216 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
6217
6218 output = check_output('ip -d link show bridge99')
6219 print(output)
6220 self.assertIn('mtu 9000 ', output)
6221
6222 output = check_output('ip -d link show test1')
6223 print(output)
6224 self.assertIn('master bridge99 ', output)
6225 self.assertIn('bridge_slave', output)
6226 self.assertIn('mtu 9000 ', output)
6227
6228 output = check_output('ip -d link show dummy98')
6229 print(output)
6230 self.assertIn('master bridge99 ', output)
6231 self.assertIn('bridge_slave', output)
6232 self.assertIn('mtu 9000 ', output)
6233
6234 output = check_output('ip addr show bridge99')
6235 print(output)
6236 self.assertIn('192.168.0.15/24', output)
6237
6238 output = check_output('bridge -d link show dummy98')
6239 print(output)
6240 self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
6241 self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
6242 self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
6243 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
6244 self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
6245 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
6246 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6247 self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True)
6248 self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
6249 self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
6250 self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
6251 self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
6252 self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
6253
6254 output = check_output('bridge -d link show test1')
6255 print(output)
6256 self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
6257 self.assertIn('locked on', output)
6258 if ' mab ' in output: # This is new in kernel and iproute2 v6.2
6259 self.assertIn('mab on', output)
6260
6261 def test_bridge_property(self):
6262 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6263 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
6264 '25-bridge99.network', '14-dummy.netdev', '26-bridge-vlan-tunnel.network')
6265 start_networkd()
6266 self.check_bridge_property()
6267
6268 # test reload
6269 touch_network_unit(
6270 '11-dummy.netdev',
6271 '12-dummy.netdev',
6272 '26-bridge.netdev',
6273 '26-bridge-slave-interface-1.network',
6274 '26-bridge-slave-interface-2.network',
6275 '26-bridge-vlan-tunnel.network',
6276 '25-bridge99.network')
6277 networkctl_reload()
6278 self.check_bridge_property()
6279
6280 check_output('ip address add 192.168.0.16/24 dev bridge99')
6281 output = check_output('ip addr show bridge99')
6282 print(output)
6283 self.assertIn('192.168.0.16/24', output)
6284
6285 # for issue #6088
6286 print('### ip -6 route list table all dev bridge99')
6287 output = check_output('ip -6 route list table all dev bridge99')
6288 print(output)
6289 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
6290
6291 remove_link('test1')
6292 self.wait_operstate('bridge99', 'routable')
6293
6294 output = check_output('ip -d link show bridge99')
6295 print(output)
6296 self.assertIn('mtu 9000 ', output)
6297
6298 output = check_output('ip -d link show dummy98')
6299 print(output)
6300 self.assertIn('master bridge99 ', output)
6301 self.assertIn('bridge_slave', output)
6302 self.assertIn('mtu 9000 ', output)
6303
6304 output = check_output('ip -d link show dummy97')
6305 self.assertIn('vlan_tunnel on ', output)
6306
6307 remove_link('dummy98')
6308 remove_link('dummy97')
6309 self.wait_operstate('bridge99', 'no-carrier')
6310
6311 output = check_output('ip -d link show bridge99')
6312 print(output)
6313 # When no carrier, the kernel may reset the MTU
6314 self.assertIn('NO-CARRIER', output)
6315
6316 output = check_output('ip address show bridge99')
6317 print(output)
6318 self.assertNotIn('192.168.0.15/24', output)
6319 self.assertIn('192.168.0.16/24', output) # foreign address is kept
6320
6321 print('### ip -6 route list table all dev bridge99')
6322 output = check_output('ip -6 route list table all dev bridge99')
6323 print(output)
6324 self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
6325
6326 check_output('ip link add dummy98 type dummy')
6327 self.wait_online('dummy98:enslaved', 'bridge99:routable')
6328
6329 output = check_output('ip -d link show bridge99')
6330 print(output)
6331 self.assertIn('mtu 9000 ', output)
6332
6333 output = check_output('ip -d link show dummy98')
6334 print(output)
6335 self.assertIn('master bridge99 ', output)
6336 self.assertIn('bridge_slave', output)
6337 self.assertIn('mtu 9000 ', output)
6338
6339 def test_bridge_configure_without_carrier(self):
6340 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
6341 '11-dummy.netdev')
6342 start_networkd()
6343
6344 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
6345 for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
6346 with self.subTest(test=test):
6347 if test == 'no-slave':
6348 # bridge has no slaves; it's up but *might* not have carrier
6349 self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
6350 # due to a bug in the kernel, newly-created bridges are brought up
6351 # *with* carrier, unless they have had any setting changed; e.g.
6352 # their mac set, priority set, etc. Then, they will lose carrier
6353 # as soon as a (down) slave interface is added, and regain carrier
6354 # again once the slave interface is brought up.
6355 #self.check_link_attr('bridge99', 'carrier', '0')
6356 elif test == 'add-slave':
6357 # add slave to bridge, but leave it down; bridge is definitely no-carrier
6358 self.check_link_attr('test1', 'operstate', 'down')
6359 check_output('ip link set dev test1 master bridge99')
6360 self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
6361 self.check_link_attr('bridge99', 'carrier', '0')
6362 elif test == 'slave-up':
6363 # bring up slave, which will have carrier; bridge gains carrier
6364 check_output('ip link set dev test1 up')
6365 self.wait_online('bridge99:routable')
6366 self.check_link_attr('bridge99', 'carrier', '1')
6367 elif test == 'slave-no-carrier':
6368 # drop slave carrier; bridge loses carrier
6369 check_output('ip link set dev test1 carrier off')
6370 self.wait_online('bridge99:no-carrier:no-carrier')
6371 self.check_link_attr('bridge99', 'carrier', '0')
6372 elif test == 'slave-carrier':
6373 # restore slave carrier; bridge gains carrier
6374 check_output('ip link set dev test1 carrier on')
6375 self.wait_online('bridge99:routable')
6376 self.check_link_attr('bridge99', 'carrier', '1')
6377 elif test == 'slave-down':
6378 # bring down slave; bridge loses carrier
6379 check_output('ip link set dev test1 down')
6380 self.wait_online('bridge99:no-carrier:no-carrier')
6381 self.check_link_attr('bridge99', 'carrier', '0')
6382
6383 output = networkctl_status('bridge99')
6384 self.assertRegex(output, '10.1.2.3')
6385 self.assertRegex(output, '10.1.2.1')
6386
6387 def test_bridge_ignore_carrier_loss(self):
6388 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
6389 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
6390 '25-bridge99-ignore-carrier-loss.network')
6391 start_networkd()
6392 self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
6393
6394 check_output('ip address add 192.168.0.16/24 dev bridge99')
6395 remove_link('test1', 'dummy98')
6396 time.sleep(3)
6397
6398 output = check_output('ip address show bridge99')
6399 print(output)
6400 self.assertRegex(output, 'NO-CARRIER')
6401 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6402 self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
6403
6404 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
6405 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
6406 '25-bridge99-ignore-carrier-loss.network')
6407 start_networkd()
6408 self.wait_online('bridge99:no-carrier')
6409
6410 for trial in range(4):
6411 check_output('ip link add dummy98 type dummy')
6412 check_output('ip link set dummy98 up')
6413 if trial < 3:
6414 remove_link('dummy98')
6415
6416 self.wait_online('bridge99:routable', 'dummy98:enslaved')
6417
6418 output = check_output('ip address show bridge99')
6419 print(output)
6420 self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
6421
6422 output = check_output('ip rule list table 100')
6423 print(output)
6424 self.assertIn('from all to 8.8.8.8 lookup 100', output)
6425
6426 class NetworkdSRIOVTests(unittest.TestCase, Utilities):
6427
6428 def setUp(self):
6429 setup_common()
6430
6431 def tearDown(self):
6432 tear_down_common()
6433
6434 def setup_netdevsim(self, id=99, num_ports=1, num_vfs=0):
6435 call('modprobe netdevsim')
6436
6437 # Create netdevsim device.
6438 with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
6439 f.write(f'{id} {num_ports}')
6440
6441 # Create VF.
6442 if num_vfs > 0:
6443 with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f:
6444 f.write(f'{num_vfs}')
6445
6446 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6447 def test_sriov(self):
6448 copy_network_unit('25-netdevsim.link', '25-sriov.network')
6449
6450 self.setup_netdevsim(num_vfs=3)
6451
6452 start_networkd()
6453 self.wait_online('sim99:routable')
6454
6455 output = check_output('ip link show dev sim99')
6456 print(output)
6457 self.assertRegex(output,
6458 '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 *'
6459 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6460 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6461 )
6462
6463 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
6464 def test_sriov_udev(self):
6465 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
6466
6467 self.setup_netdevsim()
6468
6469 start_networkd()
6470 self.wait_online('sim99:routable')
6471
6472 # The name sim99 is an alternative name, and cannot be used by udevadm below.
6473 ifname = link_resolve('sim99')
6474
6475 output = check_output('ip link show dev sim99')
6476 print(output)
6477 self.assertRegex(output,
6478 '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 *'
6479 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6480 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6481 )
6482 self.assertNotIn('vf 3', output)
6483 self.assertNotIn('vf 4', output)
6484
6485 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6486 f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
6487
6488 udevadm_reload()
6489 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6490
6491 output = check_output('ip link show dev sim99')
6492 print(output)
6493 self.assertRegex(output,
6494 '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 *'
6495 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6496 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6497 'vf 3'
6498 )
6499 self.assertNotIn('vf 4', output)
6500
6501 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6502 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6503
6504 udevadm_reload()
6505 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6506
6507 output = check_output('ip link show dev sim99')
6508 print(output)
6509 self.assertRegex(output,
6510 '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 *'
6511 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6512 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
6513 'vf 3'
6514 )
6515 self.assertNotIn('vf 4', output)
6516
6517 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6518 f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
6519
6520 udevadm_reload()
6521 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6522
6523 output = check_output('ip link show dev sim99')
6524 print(output)
6525 self.assertRegex(output,
6526 '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 *'
6527 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
6528 )
6529 self.assertNotIn('vf 2', output)
6530 self.assertNotIn('vf 3', output)
6531 self.assertNotIn('vf 4', output)
6532
6533 with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
6534 f.write('[Link]\nSR-IOVVirtualFunctions=\n')
6535
6536 udevadm_reload()
6537 udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
6538
6539 output = check_output('ip link show dev sim99')
6540 print(output)
6541 self.assertRegex(output,
6542 '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 *'
6543 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
6544 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6545 )
6546 self.assertNotIn('vf 3', output)
6547 self.assertNotIn('vf 4', output)
6548
6549 class NetworkdLLDPTests(unittest.TestCase, Utilities):
6550
6551 def setUp(self):
6552 setup_common()
6553
6554 def tearDown(self):
6555 tear_down_common()
6556
6557 def test_lldp(self):
6558 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
6559 start_networkd()
6560 self.wait_online('veth99:degraded', 'veth-peer:degraded')
6561
6562 for _ in range(20):
6563 output = networkctl('lldp')
6564 print(output)
6565 if re.search(r'veth99 .* veth-peer .* .......a...', output):
6566 break
6567 time.sleep(0.5)
6568 else:
6569 self.fail()
6570
6571 # With interface name
6572 output = networkctl('lldp', 'veth99');
6573 print(output)
6574 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
6575
6576 # With interface name pattern
6577 output = networkctl('lldp', 've*9');
6578 print(output)
6579 self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
6580
6581 # json format
6582 output = networkctl('--json=short', 'lldp')
6583 print(output)
6584 self.assertIn('"InterfaceName":"veth99"', output)
6585 self.assertIn('"PortID":"veth-peer"', output)
6586 self.assertIn('"EnabledCapabilities":128', output)
6587
6588 # json format with interface name
6589 output = networkctl('--json=short', 'lldp', 'veth99')
6590 print(output)
6591 self.assertIn('"InterfaceName":"veth99"', output)
6592 self.assertIn('"PortID":"veth-peer"', output)
6593 self.assertIn('"EnabledCapabilities":128', output)
6594
6595 # json format with interface name pattern
6596 output = networkctl('--json=short', 'lldp', 've*9')
6597 print(output)
6598 self.assertIn('"InterfaceName":"veth99"', output)
6599 self.assertIn('"PortID":"veth-peer"', output)
6600 self.assertIn('"EnabledCapabilities":128', output)
6601
6602 # LLDP neighbors in status
6603 output = networkctl_status('veth99')
6604 print(output)
6605 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6606
6607 # enable forwarding, to enable the Router flag
6608 with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
6609 f.write('[Network]\nIPv4Forwarding=yes\n')
6610
6611 networkctl_reload()
6612 self.wait_online('veth-peer:degraded')
6613
6614 for _ in range(20):
6615 output = networkctl('lldp')
6616 print(output)
6617 if re.search(r'veth99 .* veth-peer .* ....r......', output):
6618 break
6619 time.sleep(0.5)
6620 else:
6621 self.fail()
6622
6623 # With interface name
6624 output = networkctl('lldp', 'veth99');
6625 print(output)
6626 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6627
6628 # With interface name pattern
6629 output = networkctl('lldp', 've*9');
6630 print(output)
6631 self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
6632
6633 # json format
6634 output = networkctl('--json=short', 'lldp')
6635 print(output)
6636 self.assertIn('"InterfaceName":"veth99"', output)
6637 self.assertIn('"PortID":"veth-peer"', output)
6638 self.assertIn('"EnabledCapabilities":16', output)
6639
6640 # json format with interface name
6641 output = networkctl('--json=short', 'lldp', 'veth99')
6642 print(output)
6643 self.assertIn('"InterfaceName":"veth99"', output)
6644 self.assertIn('"PortID":"veth-peer"', output)
6645 self.assertIn('"EnabledCapabilities":16', output)
6646
6647 # json format with interface name pattern
6648 output = networkctl('--json=short', 'lldp', 've*9')
6649 print(output)
6650 self.assertIn('"InterfaceName":"veth99"', output)
6651 self.assertIn('"PortID":"veth-peer"', output)
6652 self.assertIn('"EnabledCapabilities":16', output)
6653
6654 # LLDP neighbors in status
6655 output = networkctl_status('veth99')
6656 print(output)
6657 self.assertRegex(output, r'Connected To: .* on port veth-peer')
6658
6659 # Compare the json output from sender and receiver
6660 sender_json = get_link_description('veth-peer')['LLDP']
6661 receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0]
6662 print(sender_json)
6663 print(receiver_json)
6664 self.assertEqual(sender_json, receiver_json)
6665
6666 class NetworkdRATests(unittest.TestCase, Utilities):
6667
6668 def setUp(self):
6669 setup_common()
6670
6671 def tearDown(self):
6672 tear_down_common()
6673
6674 def test_ipv6_prefix_delegation(self):
6675 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
6676 self.setup_nftset('addr6', 'ipv6_addr')
6677 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6678 self.setup_nftset('ifindex', 'iface_index')
6679 start_networkd()
6680 self.wait_online('veth99:routable', 'veth-peer:degraded')
6681
6682 # IPv6SendRA=yes implies IPv6Forwarding.
6683 self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
6684
6685 output = resolvectl('dns', 'veth99')
6686 print(output)
6687 self.assertRegex(output, 'fe80::')
6688 self.assertRegex(output, '2002:da8:1::1')
6689
6690 output = resolvectl('domain', 'veth99')
6691 print(output)
6692 self.assertIn('hogehoge.test', output)
6693
6694 output = networkctl_status('veth99')
6695 print(output)
6696 self.assertRegex(output, '2002:da8:1:0')
6697
6698 self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
6699 self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
6700
6701 self.check_netlabel('veth99', '2002:da8:1::/64')
6702 self.check_netlabel('veth99', '2002:da8:2::/64')
6703
6704 self.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6705 self.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6706 self.check_nftset('network6', '2002:da8:1::/64')
6707 self.check_nftset('network6', '2002:da8:2::/64')
6708 self.check_nftset('ifindex', 'veth99')
6709
6710 self.teardown_nftset('addr6', 'network6', 'ifindex')
6711
6712 def check_ipv6_token_static(self):
6713 self.wait_online('veth99:routable', 'veth-peer:degraded')
6714
6715 output = networkctl_status('veth99')
6716 print(output)
6717 self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
6718 self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
6719 self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
6720 self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
6721
6722 def test_ipv6_token_static(self):
6723 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6724 start_networkd()
6725
6726 self.check_ipv6_token_static()
6727
6728 for _ in range(20):
6729 check_output('ip link set veth99 down')
6730 check_output('ip link set veth99 up')
6731
6732 self.check_ipv6_token_static()
6733
6734 for _ in range(20):
6735 check_output('ip link set veth99 down')
6736 time.sleep(random.uniform(0, 0.1))
6737 check_output('ip link set veth99 up')
6738 time.sleep(random.uniform(0, 0.1))
6739
6740 self.check_ipv6_token_static()
6741
6742 def test_ndisc_redirect(self):
6743 if not os.path.exists(test_ndisc_send):
6744 self.skipTest(f"{test_ndisc_send} does not exist.")
6745
6746 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
6747 start_networkd()
6748
6749 self.check_ipv6_token_static()
6750
6751 # Introduce three redirect routes.
6752 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')
6753 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')
6754 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')
6755 self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6756 self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6757 self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6758
6759 # Change the target address of the redirects.
6760 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')
6761 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')
6762 self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6763 self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
6764 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)
6765 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)
6766
6767 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
6768 # Then, verify that all redirect routes and the default route are dropped.
6769 output = check_output('ip -6 address show dev veth-peer scope link')
6770 veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
6771 print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
6772 check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
6773 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
6774 self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
6775
6776 # Check if sd-radv refuses RS from the same interface.
6777 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
6778 since = datetime.datetime.now()
6779 check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
6780 self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
6781
6782 def check_ndisc_mtu(self, mtu):
6783 for _ in range(20):
6784 output = read_ipv6_sysctl_attr('veth99', 'mtu')
6785 if output == f'{mtu}':
6786 break
6787 time.sleep(0.5)
6788 else:
6789 self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
6790
6791 def test_ndisc_mtu(self):
6792 if not os.path.exists(test_ndisc_send):
6793 self.skipTest(f"{test_ndisc_send} does not exist.")
6794
6795 copy_network_unit('25-veth.netdev',
6796 '25-veth-peer-no-address.network',
6797 '25-ipv6-prefix-veth-token-static.network')
6798 start_networkd()
6799 self.wait_online('veth-peer:degraded')
6800
6801 self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
6802
6803 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
6804 self.check_ndisc_mtu(1400)
6805
6806 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
6807 self.check_ndisc_mtu(1410)
6808
6809 check_output('ip link set dev veth99 mtu 1600')
6810 check_output('ip link set dev veth-peer mtu 1600')
6811 self.check_ndisc_mtu(1410)
6812
6813 check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
6814 self.check_ndisc_mtu(1600)
6815
6816 check_output('ip link set dev veth99 mtu 1800')
6817 check_output('ip link set dev veth-peer mtu 1800')
6818 self.check_ndisc_mtu(1700)
6819
6820 def test_ipv6_token_prefixstable(self):
6821 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
6822 start_networkd()
6823 self.wait_online('veth99:routable', 'veth-peer:degraded')
6824
6825 output = check_output('ip -6 address show dev veth99')
6826 print(output)
6827 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6828 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6829
6830 with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
6831 f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
6832
6833 networkctl_reload()
6834 self.wait_online('veth99:routable')
6835
6836 output = check_output('ip -6 address show dev veth99')
6837 print(output)
6838 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
6839 self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
6840
6841 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
6842 check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
6843
6844 networkctl_reconfigure('veth99')
6845 self.wait_online('veth99:routable')
6846 self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6847 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
6848
6849 check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
6850 check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
6851
6852 networkctl_reconfigure('veth99')
6853 self.wait_online('veth99:routable')
6854 self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable
6855 self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
6856 self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
6857
6858 def test_ipv6_token_prefixstable_without_address(self):
6859 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
6860 start_networkd()
6861 self.wait_online('veth99:routable', 'veth-peer:degraded')
6862
6863 output = networkctl_status('veth99')
6864 print(output)
6865 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
6866 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
6867
6868 def test_router_hop_limit(self):
6869 copy_network_unit('25-veth-client.netdev',
6870 '25-veth-router.netdev',
6871 '26-bridge.netdev',
6872 '25-veth-bridge.network',
6873 '25-veth-client.network',
6874 '25-veth-router-hop-limit.network',
6875 '25-bridge99.network')
6876 start_networkd()
6877 self.wait_online('client:routable', 'client-p:enslaved',
6878 'router:degraded', 'router-p:enslaved',
6879 'bridge99:routable')
6880
6881 self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
6882
6883 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
6884 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
6885
6886 networkctl_reload()
6887
6888 for _ in range(20):
6889 output = read_ipv6_sysctl_attr('client', 'hop_limit')
6890 if output == '43':
6891 break
6892 time.sleep(0.5)
6893
6894 self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
6895
6896 def check_router_preference(self, suffix, metric_1, preference_1, metric_2, preference_2):
6897 self.wait_online('client:routable')
6898 self.wait_address('client', f'2002:da8:1:99:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6899 self.wait_address('client', f'2002:da8:1:98:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10)
6900 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)
6901 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)
6902
6903 print('### ip -6 route show dev client default')
6904 output = check_output('ip -6 route show dev client default')
6905 print(output)
6906 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}')
6907 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}')
6908
6909 for i in [100, 200, 300, 512, 1024, 2048]:
6910 if i not in [metric_1, metric_2]:
6911 self.assertNotIn(f'metric {i} ', output)
6912
6913 for i in ['low', 'medium', 'high']:
6914 if i not in [preference_1, preference_2]:
6915 self.assertNotIn(f'pref {i}', output)
6916
6917 def test_router_preference(self):
6918 copy_network_unit('25-veth-client.netdev',
6919 '25-veth-router-high.netdev',
6920 '25-veth-router-low.netdev',
6921 '26-bridge.netdev',
6922 '25-veth-bridge.network',
6923 '25-veth-client.network',
6924 '25-veth-router-high.network',
6925 '25-veth-router-low.network',
6926 '25-bridge99.network')
6927 start_networkd()
6928 self.wait_online('client-p:enslaved',
6929 'router-high:degraded', 'router-high-p:enslaved',
6930 'router-low:degraded', 'router-low-p:enslaved',
6931 'bridge99:routable')
6932
6933 networkctl_reconfigure('client')
6934 self.wait_online('client:routable')
6935 self.check_router_preference('00', 512, 'high', 2048, 'low')
6936
6937 # change the map from preference to metric.
6938 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
6939 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
6940 networkctl_reload()
6941 self.check_router_preference('01', 100, 'high', 300, 'low')
6942
6943 # swap the preference (for issue #28439)
6944 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6945 f.write('\n[IPv6SendRA]\nRouterPreference=low\n')
6946 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6947 f.write('\n[IPv6SendRA]\nRouterPreference=high\n')
6948 networkctl_reload()
6949 self.check_router_preference('01', 300, 'low', 100, 'high')
6950
6951 # Use the same preference, and check if the two routes are not coalesced. See issue #33470.
6952 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6953 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
6954 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6955 f.write('\n[IPv6SendRA]\nRouterPreference=medium\n')
6956 networkctl_reload()
6957 self.check_router_preference('01', 200, 'medium', 200, 'medium')
6958
6959 # Use route options to configure default routes.
6960 # The preference specified in the RA header should be ignored. See issue #33468.
6961 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6962 f.write('\n[IPv6SendRA]\nRouterPreference=high\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6963 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6964 f.write('\n[IPv6SendRA]\nRouterPreference=low\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n')
6965 networkctl_reload()
6966 self.check_router_preference('01', 200, 'medium', 200, 'medium')
6967
6968 # Set zero lifetime to the route options.
6969 # The preference specified in the RA header should be used.
6970 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6971 f.write('LifetimeSec=0\n')
6972 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6973 f.write('LifetimeSec=0\n')
6974 networkctl_reload()
6975 self.check_router_preference('01', 100, 'high', 300, 'low')
6976
6977 # Use route options with preference to configure default routes.
6978 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6979 f.write('LifetimeSec=1200\nPreference=low\n')
6980 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6981 f.write('LifetimeSec=1200\nPreference=high\n')
6982 networkctl_reload()
6983 self.check_router_preference('01', 300, 'low', 100, 'high')
6984
6985 # Set zero lifetime again to the route options.
6986 with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f:
6987 f.write('LifetimeSec=0\n')
6988 with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f:
6989 f.write('LifetimeSec=0\n')
6990 networkctl_reload()
6991 self.check_router_preference('01', 100, 'high', 300, 'low')
6992
6993 def _test_ndisc_vs_static_route(self, manage_foreign_nexthops):
6994 if not manage_foreign_nexthops:
6995 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
6996 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-static-route.network')
6997 start_networkd()
6998 self.wait_online('veth99:routable', 'veth-peer:degraded')
6999
7000 # If a conflicting static route is already configured, do not override the static route.
7001 print('### ip -6 route show dev veth99 default')
7002 output = check_output('ip -6 route show dev veth99 default')
7003 print(output)
7004 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
7005 if manage_foreign_nexthops:
7006 self.assertRegex(output, r'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium')
7007 else:
7008 self.assertNotIn('proto ra', output)
7009
7010 print('### ip -6 nexthop show dev veth99')
7011 output = check_output('ip -6 nexthop show dev veth99')
7012 print(output)
7013 if manage_foreign_nexthops:
7014 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra')
7015 else:
7016 self.assertEqual(output, '')
7017
7018 # Also check if the static route is protected from RA with zero lifetime
7019 with open(os.path.join(network_unit_dir, '25-ipv6-prefix.network'), mode='a', encoding='utf-8') as f:
7020 f.write('\n[Network]\nIPv6SendRA=no\n')
7021 networkctl_reload() # This makes veth-peer being reconfigured, and send RA with zero lifetime
7022 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)
7023
7024 print('### ip -6 route show dev veth99 default')
7025 output = check_output('ip -6 route show dev veth99 default')
7026 print(output)
7027 self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output)
7028 self.assertNotIn('proto ra', output)
7029
7030 # Check if nexthop is removed.
7031 print('### ip -6 nexthop show dev veth99')
7032 output = check_output('ip -6 nexthop show dev veth99')
7033 print(output)
7034 self.assertEqual(output, '')
7035
7036 def test_ndisc_vs_static_route(self):
7037 first = True
7038 for manage_foreign_nexthops in [True, False]:
7039 if first:
7040 first = False
7041 else:
7042 self.tearDown()
7043
7044 print(f'### test_ndisc_vs_static_route(manage_foreign_nexthops={manage_foreign_nexthops})')
7045 with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
7046 self._test_ndisc_vs_static_route(manage_foreign_nexthops)
7047
7048 # radvd supports captive portal since v2.20.
7049 # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc
7050 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7051 def test_captive_portal(self):
7052 copy_network_unit('25-veth-client.netdev',
7053 '25-veth-router-captive.netdev',
7054 '26-bridge.netdev',
7055 '25-veth-client-captive.network',
7056 '25-veth-router-captive.network',
7057 '25-veth-bridge-captive.network',
7058 '25-bridge99.network')
7059 start_networkd()
7060 self.wait_online('bridge99:routable', 'client-p:enslaved',
7061 'router-captive:degraded', 'router-captivep:enslaved')
7062
7063 start_radvd(config_file='captive-portal.conf')
7064 networkctl_reconfigure('client')
7065 self.wait_online('client:routable')
7066
7067 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
7068 output = networkctl_status('client')
7069 print(output)
7070 self.assertIn('Captive Portal: http://systemd.io', output)
7071
7072 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
7073 def test_invalid_captive_portal(self):
7074 def radvd_write_config(captive_portal_uri):
7075 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
7076 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
7077
7078 captive_portal_uris = [
7079 "42ěščěškd ěšč ě s",
7080 " ",
7081 "🤔",
7082 ]
7083
7084 copy_network_unit('25-veth-client.netdev',
7085 '25-veth-router-captive.netdev',
7086 '26-bridge.netdev',
7087 '25-veth-client-captive.network',
7088 '25-veth-router-captive.network',
7089 '25-veth-bridge-captive.network',
7090 '25-bridge99.network')
7091 start_networkd()
7092 self.wait_online('bridge99:routable', 'client-p:enslaved',
7093 'router-captive:degraded', 'router-captivep:enslaved')
7094
7095 for uri in captive_portal_uris:
7096 print(f"Captive portal: {uri}")
7097 radvd_write_config(uri)
7098 stop_radvd()
7099 start_radvd(config_file='bogus-captive-portal.conf')
7100 networkctl_reconfigure('client')
7101 self.wait_online('client:routable')
7102
7103 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
7104 output = networkctl_status('client')
7105 print(output)
7106 self.assertNotIn('Captive Portal:', output)
7107
7108 class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
7109
7110 def setUp(self):
7111 setup_common()
7112
7113 def tearDown(self):
7114 tear_down_common()
7115
7116 def check_dhcp_server(self, persist_leases='yes'):
7117 output = networkctl_status('veth99')
7118 print(output)
7119 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7120 self.assertIn('Gateway: 192.168.5.3', output)
7121 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
7122 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
7123
7124 output = networkctl_status('veth-peer')
7125 print(output)
7126 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
7127
7128 if persist_leases == 'yes':
7129 path = '/var/lib/systemd/network/dhcp-server-lease/veth-peer'
7130 elif persist_leases == 'runtime':
7131 path = '/run/systemd/netif/dhcp-server-lease/veth-peer'
7132 else:
7133 path = None
7134
7135 if path:
7136 with open(path, encoding='utf-8') as f:
7137 check_json(f.read())
7138
7139 def test_dhcp_server(self):
7140 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7141 start_networkd()
7142 self.wait_online('veth99:routable', 'veth-peer:routable')
7143
7144 self.check_dhcp_server()
7145
7146 networkctl_reconfigure('veth-peer')
7147 self.wait_online('veth-peer:routable')
7148
7149 for _ in range(10):
7150 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
7151 if 'Offered DHCP leases: 192.168.5.' in output:
7152 break
7153 time.sleep(.2)
7154 else:
7155 self.fail()
7156
7157 def test_dhcp_server_persist_leases_no(self):
7158 copy_networkd_conf_dropin('persist-leases-no.conf')
7159 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7160 start_networkd()
7161 self.wait_online('veth99:routable', 'veth-peer:routable')
7162
7163 self.check_dhcp_server(persist_leases='no')
7164
7165 remove_networkd_conf_dropin('persist-leases-no.conf')
7166 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7167 f.write('[DHCPServer]\nPersistLeases=no')
7168 restart_networkd()
7169 self.wait_online('veth99:routable', 'veth-peer:routable')
7170
7171 self.check_dhcp_server(persist_leases='no')
7172
7173 def test_dhcp_server_persist_leases_runtime(self):
7174 copy_networkd_conf_dropin('persist-leases-runtime.conf')
7175 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
7176 start_networkd()
7177 self.wait_online('veth99:routable', 'veth-peer:routable')
7178
7179 self.check_dhcp_server(persist_leases='runtime')
7180
7181 remove_networkd_conf_dropin('persist-leases-runtime.conf')
7182 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
7183 f.write('[DHCPServer]\nPersistLeases=runtime')
7184 restart_networkd()
7185 self.wait_online('veth99:routable', 'veth-peer:routable')
7186
7187 self.check_dhcp_server(persist_leases='runtime')
7188
7189 def test_dhcp_server_null_server_address(self):
7190 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
7191 start_networkd()
7192 self.wait_online('veth99:routable', 'veth-peer:routable')
7193
7194 output = check_output('ip --json address show dev veth-peer')
7195 server_address = json.loads(output)[0]['addr_info'][0]['local']
7196 print(server_address)
7197
7198 output = check_output('ip --json address show dev veth99')
7199 client_address = json.loads(output)[0]['addr_info'][0]['local']
7200 print(client_address)
7201
7202 output = networkctl_status('veth99')
7203 print(output)
7204 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
7205 self.assertIn(f'Gateway: {server_address}', output)
7206 self.assertIn(f'DNS: {server_address}', output)
7207 self.assertIn(f'NTP: {server_address}', output)
7208
7209 output = networkctl_status('veth-peer')
7210 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7211
7212 # Check if the same addresses are used even if the service is restarted.
7213 restart_networkd()
7214 self.wait_online('veth99:routable', 'veth-peer:routable')
7215
7216 output = check_output('ip -4 address show dev veth-peer')
7217 print(output)
7218 self.assertIn(f'{server_address}', output)
7219
7220 output = check_output('ip -4 address show dev veth99')
7221 print(output)
7222 self.assertIn(f'{client_address}', output)
7223
7224 output = networkctl_status('veth99')
7225 print(output)
7226 self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
7227 self.assertIn(f'Gateway: {server_address}', output)
7228 self.assertIn(f'DNS: {server_address}', output)
7229 self.assertIn(f'NTP: {server_address}', output)
7230
7231 output = networkctl_status('veth-peer')
7232 self.assertIn(f'Offered DHCP leases: {client_address}', output)
7233
7234 def test_dhcp_server_with_uplink(self):
7235 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
7236 '12-dummy.netdev', '25-dhcp-server-uplink.network')
7237 start_networkd()
7238 self.wait_online('veth99:routable', 'veth-peer:routable')
7239
7240 output = networkctl_status('veth99')
7241 print(output)
7242 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
7243 self.assertIn('Gateway: 192.168.5.3', output)
7244 self.assertIn('DNS: 192.168.5.1', output)
7245 self.assertIn('NTP: 192.168.5.1', output)
7246
7247 def test_emit_router_timezone(self):
7248 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.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.1', output)
7256 self.assertIn('Time Zone: Europe/Berlin', output)
7257
7258 def test_dhcp_server_static_lease_mac_by_network(self):
7259 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
7260 copy_networkd_conf_dropin('10-dhcp-client-id-duid.conf')
7261 start_networkd()
7262 self.wait_online('veth99:routable', 'veth-peer:routable')
7263
7264 output = networkctl_status('veth99')
7265 print(output)
7266 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7267 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
7268
7269 def test_dhcp_server_static_lease_mac_by_global(self):
7270 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7271 copy_networkd_conf_dropin('10-dhcp-client-id-mac.conf')
7272 start_networkd()
7273 self.wait_online('veth99:routable', 'veth-peer:routable')
7274
7275 output = networkctl_status('veth99')
7276 print(output)
7277 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7278 self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
7279
7280 def test_dhcp_server_static_lease_duid(self):
7281 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
7282 start_networkd()
7283 self.wait_online('veth99:routable', 'veth-peer:routable')
7284
7285 output = networkctl_status('veth99')
7286 print(output)
7287 self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
7288 self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
7289
7290 class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
7291
7292 def setUp(self):
7293 setup_common()
7294
7295 def tearDown(self):
7296 tear_down_common()
7297
7298 def test_relay_agent(self):
7299 copy_network_unit('25-agent-veth-client.netdev',
7300 '25-agent-veth-server.netdev',
7301 '25-agent-client.network',
7302 '25-agent-server.network',
7303 '25-agent-client-peer.network',
7304 '25-agent-server-peer.network')
7305 start_networkd()
7306
7307 self.wait_online('client:routable')
7308
7309 output = networkctl_status('client')
7310 print(output)
7311 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)')
7312
7313 def test_relay_agent_on_bridge(self):
7314 copy_network_unit('25-agent-bridge.netdev',
7315 '25-agent-veth-client.netdev',
7316 '25-agent-bridge.network',
7317 '25-agent-bridge-port.network',
7318 '25-agent-client.network')
7319 start_networkd()
7320 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
7321
7322 # For issue #30763.
7323 self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
7324
7325 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
7326
7327 def setUp(self):
7328 setup_common()
7329
7330 def tearDown(self):
7331 tear_down_common()
7332
7333 def test_dhcp_client_ipv6_only(self):
7334 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
7335
7336 start_networkd()
7337 self.wait_online('veth-peer:carrier')
7338
7339 # information request mode
7340 # The name ipv6-only option may not be supported by older dnsmasq
7341 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
7342 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7343 '--dhcp-option=option6:dns-server,[2600::ee]',
7344 '--dhcp-option=option6:ntp-server,[2600::ff]',
7345 ra_mode='ra-stateless')
7346 self.wait_online('veth99:routable', 'veth-peer:routable')
7347
7348 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
7349 # Let's wait for the expected DNS server being listed in the state file.
7350 for _ in range(100):
7351 output = read_link_state_file('veth99')
7352 if 'DNS=2600::ee' in output:
7353 break
7354 time.sleep(.2)
7355
7356 # Check link state file
7357 print('## link state file')
7358 output = read_link_state_file('veth99')
7359 print(output)
7360 self.assertIn('DNS=2600::ee', output)
7361 self.assertIn('NTP=2600::ff', output)
7362
7363 # Check manager state file
7364 print('## manager state file')
7365 output = read_manager_state_file()
7366 print(output)
7367 self.assertRegex(output, 'DNS=.*2600::ee')
7368 self.assertRegex(output, 'NTP=.*2600::ff')
7369
7370 print('## dnsmasq log')
7371 output = read_dnsmasq_log_file()
7372 print(output)
7373 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7374 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
7375 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7376 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7377 self.assertNotIn('DHCPREPLY(veth-peer)', output)
7378
7379 # Check json format
7380 check_json(networkctl_json('veth99'))
7381
7382 # solicit mode
7383 stop_dnsmasq()
7384 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7385 '--dhcp-option=option6:dns-server,[2600::ee]',
7386 '--dhcp-option=option6:ntp-server,[2600::ff]')
7387 networkctl_reconfigure('veth99')
7388 self.wait_online('veth99:routable', 'veth-peer:routable')
7389
7390 # checking address
7391 output = check_output('ip address show dev veth99 scope global')
7392 print(output)
7393 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7394 self.assertNotIn('192.168.5', output)
7395
7396 # checking semi-static route
7397 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7398 print(output)
7399 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7400
7401 # Confirm that ipv6 token is not set in the kernel
7402 output = check_output('ip token show dev veth99')
7403 print(output)
7404 self.assertRegex(output, 'token :: dev veth99')
7405
7406 # Make manager and link state file updated
7407 resolvectl('revert', 'veth99')
7408
7409 # Check link state file
7410 print('## link state file')
7411 output = read_link_state_file('veth99')
7412 print(output)
7413 self.assertIn('DNS=2600::ee', output)
7414 self.assertIn('NTP=2600::ff', output)
7415
7416 # Check manager state file
7417 print('## manager state file')
7418 output = read_manager_state_file()
7419 print(output)
7420 self.assertRegex(output, 'DNS=.*2600::ee')
7421 self.assertRegex(output, 'NTP=.*2600::ff')
7422
7423 print('## dnsmasq log')
7424 output = read_dnsmasq_log_file()
7425 print(output)
7426 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7427 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7428 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7429 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7430 self.assertIn('DHCPREPLY(veth-peer)', output)
7431 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7432
7433 # Check json format
7434 check_json(networkctl_json('veth99'))
7435
7436 # Testing without rapid commit support
7437 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
7438 f.write('\n[DHCPv6]\nRapidCommit=no\n')
7439
7440 stop_dnsmasq()
7441 start_dnsmasq('--dhcp-option=108,00:00:02:00',
7442 '--dhcp-option=option6:dns-server,[2600::ee]',
7443 '--dhcp-option=option6:ntp-server,[2600::ff]')
7444
7445 networkctl_reload()
7446 self.wait_online('veth99:routable', 'veth-peer:routable')
7447
7448 # checking address
7449 output = check_output('ip address show dev veth99 scope global')
7450 print(output)
7451 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7452 self.assertNotIn('192.168.5', output)
7453
7454 # checking semi-static route
7455 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
7456 print(output)
7457 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
7458
7459 # Make manager and link state file updated
7460 resolvectl('revert', 'veth99')
7461
7462 # Check link state file
7463 print('## link state file')
7464 output = read_link_state_file('veth99')
7465 print(output)
7466 self.assertIn('DNS=2600::ee', output)
7467 self.assertIn('NTP=2600::ff', output)
7468
7469 # Check manager state file
7470 print('## manager state file')
7471 output = read_manager_state_file()
7472 print(output)
7473 self.assertRegex(output, 'DNS=.*2600::ee')
7474 self.assertRegex(output, 'NTP=.*2600::ff')
7475
7476 print('## dnsmasq log')
7477 output = read_dnsmasq_log_file()
7478 print(output)
7479 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
7480 self.assertIn('DHCPSOLICIT(veth-peer)', output)
7481 self.assertIn('DHCPADVERTISE(veth-peer)', output)
7482 self.assertIn('DHCPREQUEST(veth-peer)', output)
7483 self.assertIn('DHCPREPLY(veth-peer)', output)
7484 self.assertNotIn('rapid-commit', output)
7485
7486 # Check json format
7487 check_json(networkctl_json('veth99'))
7488
7489 def test_dhcp_client_ipv6_dbus_status(self):
7490 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
7491 start_networkd()
7492 self.wait_online('veth-peer:carrier')
7493
7494 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
7495 # bit set) has yet been received and the configuration does not include WithoutRA=true
7496 state = get_dhcp6_client_state('veth99')
7497 print(f"DHCPv6 client state = {state}")
7498 self.assertEqual(state, 'stopped')
7499
7500 state = get_dhcp4_client_state('veth99')
7501 print(f"DHCPv4 client state = {state}")
7502 self.assertEqual(state, 'selecting')
7503
7504 start_dnsmasq('--dhcp-option=108,00:00:02:00')
7505 self.wait_online('veth99:routable', 'veth-peer:routable')
7506
7507 state = get_dhcp6_client_state('veth99')
7508 print(f"DHCPv6 client state = {state}")
7509 self.assertEqual(state, 'bound')
7510
7511 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
7512 for _ in range(100):
7513 state = get_dhcp4_client_state('veth99')
7514 if state == 'stopped':
7515 break
7516 time.sleep(.2)
7517
7518 print(f"DHCPv4 client state = {state}")
7519 self.assertEqual(state, 'stopped')
7520
7521 # restart dnsmasq to clear log
7522 stop_dnsmasq()
7523 start_dnsmasq('--dhcp-option=108,00:00:02:00')
7524
7525 # Test renew command
7526 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
7527 networkctl('renew', 'veth99')
7528
7529 for _ in range(100):
7530 state = get_dhcp4_client_state('veth99')
7531 if state == 'stopped':
7532 break
7533 time.sleep(.2)
7534
7535 print(f"DHCPv4 client state = {state}")
7536 self.assertEqual(state, 'stopped')
7537
7538 print('## dnsmasq log')
7539 output = read_dnsmasq_log_file()
7540 print(output)
7541 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
7542 self.assertIn('DHCPOFFER(veth-peer)', output)
7543 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7544 self.assertNotIn('DHCPACK(veth-peer)', output)
7545
7546 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
7547 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
7548
7549 start_networkd()
7550 self.wait_online('veth-peer:carrier')
7551 start_dnsmasq()
7552 self.wait_online('veth99:routable', 'veth-peer:routable')
7553
7554 # checking address
7555 output = check_output('ip address show dev veth99 scope global')
7556 print(output)
7557 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
7558 self.assertNotIn('192.168.5', output)
7559
7560 print('## dnsmasq log')
7561 output = read_dnsmasq_log_file()
7562 print(output)
7563 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
7564 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
7565 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7566 self.assertIn('DHCPREPLY(veth-peer)', output)
7567 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
7568
7569 @expectedFailureIfKernelReturnsInvalidFlags()
7570 def test_dhcp_client_ipv4_only(self):
7571 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network',
7572 '25-sit-dhcp4.netdev', '25-sit-dhcp4.network')
7573
7574 self.setup_nftset('addr4', 'ipv4_addr')
7575 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
7576 self.setup_nftset('ifindex', 'iface_index')
7577
7578 start_networkd()
7579 self.wait_online('veth-peer:carrier')
7580 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
7581 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
7582 '--dhcp-option=option:domain-search,example.com',
7583 '--dhcp-alternate-port=67,5555',
7584 ipv4_range='192.168.5.110,192.168.5.119')
7585 self.wait_online('veth99:routable', 'veth-peer:routable', 'sit-dhcp4:carrier')
7586 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
7587
7588 print('## ip address show dev veth99 scope global')
7589 output = check_output('ip address show dev veth99 scope global')
7590 print(output)
7591 self.assertIn('mtu 1492', output)
7592 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
7593 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')
7594 self.assertNotIn('2600::', output)
7595
7596 output = check_output('ip -4 --json address show dev veth99')
7597 for i in json.loads(output)[0]['addr_info']:
7598 if i['label'] == 'test-label':
7599 address1 = i['local']
7600 break
7601 else:
7602 self.assertFalse(True)
7603
7604 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
7605
7606 print('## ip route show table main dev veth99')
7607 output = check_output('ip route show table main dev veth99')
7608 print(output)
7609 # no DHCP routes assigned to the main table
7610 self.assertNotIn('proto dhcp', output)
7611 # static routes
7612 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7613 self.assertIn('192.168.5.0/24 proto static scope link', output)
7614 self.assertIn('192.168.6.0/24 proto static scope link', output)
7615 self.assertIn('192.168.7.0/24 proto static scope link', output)
7616
7617 print('## ip route show table 211 dev veth99')
7618 output = check_output('ip route show table 211 dev veth99')
7619 print(output)
7620 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
7621 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7622 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7623 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
7624 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
7625 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1}')
7626
7627 print('## ip route show table 212 dev veth99')
7628 output = check_output('ip route show table 212 dev veth99')
7629 print(output)
7630 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
7631 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
7632 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1}')
7633
7634 print('## link state file')
7635 output = read_link_state_file('veth99')
7636 print(output)
7637 # checking DNS server, SIP server, and Domains
7638 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
7639 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
7640 self.assertIn('DOMAINS=example.com', output)
7641
7642 print('## json')
7643 j = json.loads(networkctl_json('veth99'))
7644
7645 self.assertEqual(len(j['DNS']), 2)
7646 for i in j['DNS']:
7647 print(i)
7648 self.assertEqual(i['Family'], 2)
7649 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7650 self.assertRegex(a, '^192.168.5.[67]$')
7651 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7652 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7653 self.assertEqual('192.168.5.1', a)
7654
7655 self.assertEqual(len(j['SIP']), 2)
7656 for i in j['SIP']:
7657 print(i)
7658 self.assertEqual(i['Family'], 2)
7659 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7660 self.assertRegex(a, '^192.168.5.2[12]$')
7661 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7662 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7663 self.assertEqual('192.168.5.1', a)
7664
7665 print('## tunnel')
7666 output = check_output('ip -d link show sit-dhcp4')
7667 print(output)
7668 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address1} dev veth99')
7669
7670 print('## dnsmasq log')
7671 output = read_dnsmasq_log_file()
7672 print(output)
7673 self.assertIn('vendor class: FooBarVendorTest', output)
7674 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
7675 self.assertIn('client provides name: test-hostname', output)
7676 self.assertIn('26:mtu', output)
7677
7678 # change address range, DNS servers, and Domains
7679 stop_dnsmasq()
7680 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
7681 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
7682 '--dhcp-option=option:domain-search,foo.example.com',
7683 '--dhcp-alternate-port=67,5555',
7684 ipv4_range='192.168.5.120,192.168.5.129',)
7685
7686 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
7687 print('Wait for the DHCP lease to be expired')
7688 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
7689 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
7690
7691 self.wait_online('veth99:routable', 'veth-peer:routable')
7692
7693 print('## ip address show dev veth99 scope global')
7694 output = check_output('ip address show dev veth99 scope global')
7695 print(output)
7696 self.assertIn('mtu 1492', output)
7697 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
7698 self.assertNotIn(f'{address1}', output)
7699 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')
7700 self.assertNotIn('2600::', output)
7701
7702 output = check_output('ip -4 --json address show dev veth99')
7703 for i in json.loads(output)[0]['addr_info']:
7704 if i['label'] == 'test-label':
7705 address2 = i['local']
7706 break
7707 else:
7708 self.assertFalse(True)
7709
7710 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
7711
7712 print('## ip route show table main dev veth99')
7713 output = check_output('ip route show table main dev veth99')
7714 print(output)
7715 # no DHCP routes assigned to the main table
7716 self.assertNotIn('proto dhcp', output)
7717 # static routes
7718 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
7719 self.assertIn('192.168.5.0/24 proto static scope link', output)
7720 self.assertIn('192.168.6.0/24 proto static scope link', output)
7721 self.assertIn('192.168.7.0/24 proto static scope link', output)
7722
7723 print('## ip route show table 211 dev veth99')
7724 output = check_output('ip route show table 211 dev veth99')
7725 print(output)
7726 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
7727 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7728 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
7729 self.assertNotIn('192.168.5.6', output)
7730 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
7731 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
7732 self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2}')
7733
7734 print('## ip route show table 212 dev veth99')
7735 output = check_output('ip route show table 212 dev veth99')
7736 print(output)
7737 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
7738 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
7739 self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2}')
7740
7741 print('## link state file')
7742 output = read_link_state_file('veth99')
7743 print(output)
7744 # checking DNS server, SIP server, and Domains
7745 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
7746 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
7747 self.assertIn('DOMAINS=foo.example.com', output)
7748
7749 print('## json')
7750 j = json.loads(networkctl_json('veth99'))
7751
7752 self.assertEqual(len(j['DNS']), 3)
7753 for i in j['DNS']:
7754 print(i)
7755 self.assertEqual(i['Family'], 2)
7756 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7757 self.assertRegex(a, '^192.168.5.[178]$')
7758 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7759 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7760 self.assertEqual('192.168.5.1', a)
7761
7762 self.assertEqual(len(j['SIP']), 2)
7763 for i in j['SIP']:
7764 print(i)
7765 self.assertEqual(i['Family'], 2)
7766 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
7767 self.assertRegex(a, '^192.168.5.2[34]$')
7768 self.assertEqual(i['ConfigSource'], 'DHCPv4')
7769 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
7770 self.assertEqual('192.168.5.1', a)
7771
7772 print('## tunnel')
7773 output = check_output('ip -d link show sit-dhcp4')
7774 print(output)
7775 self.assertRegex(output, fr'sit (ip6ip )?remote any local {address2} dev veth99')
7776
7777 print('## dnsmasq log')
7778 output = read_dnsmasq_log_file()
7779 print(output)
7780 self.assertIn('vendor class: FooBarVendorTest', output)
7781 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
7782 self.assertIn('client provides name: test-hostname', output)
7783 self.assertIn('26:mtu', output)
7784
7785 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
7786
7787 self.check_nftset('addr4', r'192\.168\.5\.1')
7788 self.check_nftset('network4', r'192\.168\.5\.0/24')
7789 self.check_nftset('ifindex', 'veth99')
7790
7791 # Check if DHCPv4 address and routes are removed on stop. For issue #34837.
7792 stop_networkd(show_logs=False)
7793 self.wait_address_dropped('veth99', f'inet {address2}/24', ipv='-4', timeout_sec=120)
7794
7795 print('## ip address show dev veth99 scope global')
7796 output = check_output('ip address show dev veth99 scope global')
7797 print(output)
7798 self.assertNotIn(f'{address2}', output)
7799
7800 print('## ip route show table main dev veth99')
7801 output = check_output('ip route show table main dev veth99')
7802 print(output)
7803 self.assertNotIn(f'{address2}', output)
7804
7805 print('## ip route show table 211 dev veth99')
7806 output = check_output('ip route show table 211 dev veth99')
7807 print(output)
7808 self.assertNotIn(f'{address2}', output)
7809
7810 print('## ip route show table 212 dev veth99')
7811 output = check_output('ip route show table 212 dev veth99')
7812 print(output)
7813 self.assertNotIn(f'{address2}', output)
7814
7815 self.teardown_nftset('addr4', 'network4', 'ifindex')
7816
7817 def test_dhcp_client_ipv4_dbus_status(self):
7818 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
7819 start_networkd()
7820 self.wait_online('veth-peer:carrier')
7821
7822 state = get_dhcp4_client_state('veth99')
7823 print(f"State = {state}")
7824 self.assertEqual(state, 'rebooting')
7825
7826 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
7827 '--dhcp-option=option:domain-search,example.com',
7828 '--dhcp-alternate-port=67,5555',
7829 ipv4_range='192.168.5.110,192.168.5.119')
7830 self.wait_online('veth99:routable', 'veth-peer:routable')
7831 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
7832
7833 state = get_dhcp4_client_state('veth99')
7834 print(f"State = {state}")
7835 self.assertEqual(state, 'bound')
7836
7837 def test_dhcp_client_allow_list(self):
7838 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
7839
7840 start_networkd()
7841 self.wait_online('veth-peer:carrier')
7842 since = datetime.datetime.now()
7843 start_dnsmasq()
7844
7845 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
7846
7847 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
7848 since = datetime.datetime.now()
7849 networkctl_reload()
7850
7851 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
7852
7853 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
7854 since = datetime.datetime.now()
7855 networkctl_reload()
7856
7857 self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
7858
7859 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
7860 def test_dhcp_client_rapid_commit(self):
7861 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
7862 start_networkd()
7863 self.wait_online('veth-peer:carrier')
7864
7865 start_dnsmasq('--dhcp-rapid-commit')
7866 self.wait_online('veth99:routable', 'veth-peer:routable')
7867 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7868
7869 state = get_dhcp4_client_state('veth99')
7870 print(f"DHCPv4 client state = {state}")
7871 self.assertEqual(state, 'bound')
7872
7873 output = read_dnsmasq_log_file()
7874 self.assertIn('DHCPDISCOVER(veth-peer)', output)
7875 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7876 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7877 self.assertIn('DHCPACK(veth-peer)', output)
7878
7879 def check_bootp_client(self, check_log):
7880 self.wait_online('veth99:routable', 'veth-peer:routable')
7881 output = check_output('ip -4 address show dev veth99')
7882 print(output)
7883 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24')
7884
7885 state = get_dhcp4_client_state('veth99')
7886 print(f"DHCPv4 client state = {state}")
7887 self.assertEqual(state, 'bound')
7888
7889 if check_log:
7890 output = read_dnsmasq_log_file()
7891 print(output)
7892 self.assertIn('BOOTP(veth-peer)', output)
7893 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7894 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7895 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
7896 self.assertNotIn('DHCPACK(veth-peer)', output)
7897
7898 def test_bootp_client(self):
7899 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-bootp-client.network')
7900 start_networkd()
7901 self.wait_online('veth-peer:carrier')
7902 start_dnsmasq('--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule')
7903 self.check_bootp_client(check_log=True)
7904
7905 touch_network_unit('25-bootp-client.network')
7906 networkctl_reload()
7907 self.check_bootp_client(check_log=True)
7908
7909 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7910 f.write('[DHCPv4]\nBOOTP=no\n')
7911
7912 networkctl_reload()
7913 self.check_bootp_client(check_log=False)
7914
7915 output = read_dnsmasq_log_file()
7916 print(output)
7917 # Note, on reload, the DHCP client will be started from INIT-REBOOT state,
7918 # hence DISCOVER and OFFER message will not be sent/received.
7919 self.assertNotIn('DHCPDISCOVER(veth-peer)', output)
7920 self.assertNotIn('DHCPOFFER(veth-peer)', output)
7921 self.assertIn('DHCPREQUEST(veth-peer)', output)
7922 self.assertIn('DHCPACK(veth-peer)', output)
7923
7924 with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f:
7925 f.write('[DHCPv4]\nBOOTP=yes\n')
7926
7927 since = datetime.datetime.now()
7928
7929 networkctl_reload()
7930 self.check_bootp_client(check_log=False)
7931
7932 # Check if the client send RELEASE message of the previous lease
7933 self.check_networkd_log('veth99: DHCPv4 client: RELEASE', since=since)
7934
7935 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
7936 copy_network_unit('25-veth.netdev',
7937 '25-dhcp-server-ipv6-only-mode.network',
7938 '25-dhcp-client-ipv6-only-mode.network')
7939 start_networkd()
7940 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
7941 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
7942
7943 state = get_dhcp4_client_state('veth99')
7944 print(f"State = {state}")
7945 self.assertEqual(state, 'bound')
7946
7947 def test_dhcp_client_ipv4_use_routes_gateway(self):
7948 first = True
7949 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
7950 if first:
7951 first = False
7952 else:
7953 self.tearDown()
7954
7955 print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
7956 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
7957 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
7958
7959 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
7960 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
7961 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
7962 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
7963 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
7964 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
7965 copy_network_unit(*testunits, copy_dropins=False)
7966
7967 start_networkd()
7968 self.wait_online('veth-peer:carrier')
7969 additional_options = [
7970 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
7971 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
7972 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
7973 ]
7974 if classless:
7975 additional_options += [
7976 '--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'
7977 ]
7978 start_dnsmasq(*additional_options)
7979 self.wait_online('veth99:routable', 'veth-peer:routable')
7980
7981 output = check_output('ip -4 route show dev veth99')
7982 print(output)
7983
7984 # Check UseRoutes=
7985 if use_routes:
7986 if classless:
7987 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
7988 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
7989 self.assertRegex(output, r'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
7990 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7991 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7992 else:
7993 self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
7994 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
7995 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7996 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
7997 else:
7998 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
7999 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8000 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8001 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8002 self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
8003 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8004 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8005 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8006
8007 # Check UseGateway=
8008 if use_gateway and (not classless or not use_routes):
8009 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8010 else:
8011 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8012
8013 # Check route to gateway
8014 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
8015 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8016 else:
8017 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8018
8019 # Check RoutesToDNS= and RoutesToNTP=
8020 if dns_and_ntp_routes:
8021 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8022 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8023 if use_routes:
8024 if classless:
8025 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
8026 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
8027 else:
8028 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
8029 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8030 else:
8031 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8032 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
8033 else:
8034 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8035 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
8036 self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
8037 self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
8038
8039 check_json(networkctl_json())
8040
8041 def test_dhcp_client_settings_anonymize(self):
8042 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
8043 start_networkd()
8044 self.wait_online('veth-peer:carrier')
8045 start_dnsmasq()
8046 self.wait_online('veth99:routable', 'veth-peer:routable')
8047
8048 print('## dnsmasq log')
8049 output = read_dnsmasq_log_file()
8050 print(output)
8051 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
8052 self.assertNotIn('test-hostname', output)
8053 self.assertNotIn('26:mtu', output)
8054
8055 def test_dhcp_keep_configuration_dynamic(self):
8056 copy_network_unit('25-veth.netdev',
8057 '25-dhcp-server-veth-peer.network',
8058 '25-dhcp-client-keep-configuration-dynamic.network')
8059 start_networkd()
8060 self.wait_online('veth-peer:carrier')
8061 start_dnsmasq()
8062 self.wait_online('veth99:routable', 'veth-peer:routable')
8063
8064 output = check_output('ip address show dev veth99 scope global')
8065 print(output)
8066 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8067 'valid_lft forever preferred_lft forever')
8068
8069 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
8070 stop_dnsmasq()
8071
8072 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
8073 print('Wait for the DHCP lease to be expired')
8074 time.sleep(120)
8075
8076 # The lease address should be kept after the lease expired
8077 output = check_output('ip address show dev veth99 scope global')
8078 print(output)
8079 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8080 'valid_lft forever preferred_lft forever')
8081
8082 stop_networkd()
8083
8084 # The lease address should be kept after networkd stopped
8085 output = check_output('ip address show dev veth99 scope global')
8086 print(output)
8087 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8088 'valid_lft forever preferred_lft forever')
8089
8090 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f:
8091 f.write('[Network]\nDHCP=no\n')
8092
8093 start_networkd()
8094 self.wait_online('veth99:routable', 'veth-peer:routable')
8095
8096 # Still the lease address should be kept after networkd restarted
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 def test_dhcp_keep_configuration_dynamic_on_stop(self):
8103 copy_network_unit('25-veth.netdev',
8104 '25-dhcp-server-veth-peer.network',
8105 '25-dhcp-client-keep-configuration-dynamic-on-stop.network')
8106 start_networkd()
8107 self.wait_online('veth-peer:carrier')
8108 start_dnsmasq()
8109 self.wait_online('veth99:routable', 'veth-peer:routable')
8110
8111 output = check_output('ip address show dev veth99 scope global')
8112 print(output)
8113 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8114
8115 stop_dnsmasq()
8116 stop_networkd()
8117
8118 output = check_output('ip address show dev veth99 scope global')
8119 print(output)
8120 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8121
8122 start_networkd()
8123 self.wait_online('veth-peer:routable')
8124
8125 output = check_output('ip address show dev veth99 scope global')
8126 print(output)
8127 self.assertNotIn('192.168.5.', output)
8128
8129 def test_dhcp_client_reuse_address_as_static(self):
8130 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
8131 start_networkd()
8132 self.wait_online('veth-peer:carrier')
8133 start_dnsmasq()
8134 self.wait_online('veth99:routable', 'veth-peer:routable')
8135
8136 # link become 'routable' when at least one protocol provide an valid address.
8137 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8138 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8139
8140 output = check_output('ip address show dev veth99 scope global')
8141 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
8142 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
8143 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
8144 print(static_network)
8145
8146 remove_network_unit('25-dhcp-client.network')
8147
8148 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
8149 f.write(static_network)
8150
8151 restart_networkd()
8152 self.wait_online('veth99:routable')
8153
8154 output = check_output('ip -4 address show dev veth99 scope global')
8155 print(output)
8156 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
8157 'valid_lft forever preferred_lft forever')
8158
8159 output = check_output('ip -6 address show dev veth99 scope global')
8160 print(output)
8161 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
8162 'valid_lft forever preferred_lft forever')
8163
8164 @expectedFailureIfModuleIsNotAvailable('vrf')
8165 def test_dhcp_client_vrf(self):
8166 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
8167 '25-vrf.netdev', '25-vrf.network')
8168 start_networkd()
8169 self.wait_online('veth-peer:carrier')
8170 start_dnsmasq()
8171 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
8172
8173 # link become 'routable' when at least one protocol provide an valid address.
8174 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8175 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8176
8177 print('## ip -d link show dev vrf99')
8178 output = check_output('ip -d link show dev vrf99')
8179 print(output)
8180 self.assertRegex(output, 'vrf table 42')
8181
8182 print('## ip address show vrf vrf99')
8183 output = check_output('ip address show vrf vrf99')
8184 print(output)
8185 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8186 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8187 self.assertRegex(output, 'inet6 .* scope link')
8188
8189 print('## ip address show dev veth99')
8190 output = check_output('ip address show dev veth99')
8191 print(output)
8192 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
8193 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8194 self.assertRegex(output, 'inet6 .* scope link')
8195
8196 print('## ip route show vrf vrf99')
8197 output = check_output('ip route show vrf vrf99')
8198 print(output)
8199 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
8200 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
8201 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
8202
8203 print('## ip route show table main dev veth99')
8204 output = check_output('ip route show table main dev veth99')
8205 print(output)
8206 self.assertEqual(output, '')
8207
8208 def test_dhcp_client_gateway_onlink_implicit(self):
8209 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8210 '25-dhcp-client-gateway-onlink-implicit.network')
8211 start_networkd()
8212 self.wait_online('veth-peer:carrier')
8213 start_dnsmasq()
8214 self.wait_online('veth99:routable', 'veth-peer:routable')
8215
8216 output = networkctl_status('veth99')
8217 print(output)
8218 self.assertRegex(output, '192.168.5')
8219
8220 output = check_output('ip route list dev veth99 10.0.0.0/8')
8221 print(output)
8222 self.assertRegex(output, 'onlink')
8223 output = check_output('ip route list dev veth99 192.168.100.0/24')
8224 print(output)
8225 self.assertRegex(output, 'onlink')
8226
8227 def test_dhcp_client_with_ipv4ll(self):
8228 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
8229 '25-dhcp-client-with-ipv4ll.network')
8230 start_networkd()
8231 # we need to increase timeout above default, as this will need to wait for
8232 # systemd-networkd to get the dhcpv4 transient failure event
8233 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
8234
8235 output = check_output('ip -4 address show dev veth99')
8236 print(output)
8237 self.assertNotIn('192.168.5.', output)
8238 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
8239
8240 start_dnsmasq()
8241 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
8242 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
8243 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')
8244 self.wait_online('veth99:routable')
8245
8246 output = check_output('ip -4 address show dev veth99')
8247 print(output)
8248 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
8249 self.assertNotIn('169.254.', output)
8250 self.assertNotIn('scope link', output)
8251
8252 stop_dnsmasq()
8253 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
8254 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)
8255 self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
8256
8257 output = check_output('ip -4 address show dev veth99')
8258 print(output)
8259 self.assertNotIn('192.168.5.', output)
8260 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
8261
8262 def test_dhcp_client_use_dns(self):
8263 def check(self, ipv4, ipv6, needs_reconfigure=False):
8264 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8265 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8266 f.write('[DHCPv4]\nUseDNS=')
8267 f.write('yes' if ipv4 else 'no')
8268 f.write('\n[DHCPv6]\nUseDNS=')
8269 f.write('yes' if ipv6 else 'no')
8270 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8271
8272 networkctl_reload()
8273 if needs_reconfigure:
8274 networkctl_reconfigure('veth99')
8275 self.wait_online('veth99:routable')
8276
8277 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8278 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8279 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8280
8281 # make resolved re-read the link state file
8282 resolvectl('revert', 'veth99')
8283
8284 output = resolvectl('dns', 'veth99')
8285 print(output)
8286 if ipv4:
8287 self.assertIn('192.168.5.1', output)
8288 else:
8289 self.assertNotIn('192.168.5.1', output)
8290 if ipv6:
8291 self.assertIn('2600::1', output)
8292 else:
8293 self.assertNotIn('2600::1', output)
8294
8295 check_json(networkctl_json())
8296
8297 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8298
8299 start_networkd()
8300 self.wait_online('veth-peer:carrier')
8301 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8302 '--dhcp-option=option6:dns-server,[2600::1]')
8303
8304 check(self, True, True)
8305 check(self, True, False)
8306 check(self, False, True, needs_reconfigure=True)
8307 check(self, False, False)
8308
8309 def test_dhcp_client_default_use_domains(self):
8310 def check(self, common, ipv4, ipv6):
8311 mkdir_p(networkd_conf_dropin_dir)
8312 with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
8313 f.write('[Network]\nUseDomains=')
8314 f.write('yes\n' if common else 'no\n')
8315 f.write('[DHCPv4]\nUseDomains=')
8316 f.write('yes\n' if ipv4 else 'no\n')
8317 f.write('[DHCPv6]\nUseDomains=')
8318 f.write('yes\n' if ipv6 else 'no\n')
8319
8320 restart_networkd()
8321 self.wait_online('veth-peer:carrier')
8322 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
8323 '--dhcp-option=option6:dns-server,[2600::1]',
8324 '--dhcp-option=option:domain-search,example.com',
8325 '--dhcp-option=option6:domain-search,example.com')
8326
8327 self.wait_online('veth99:routable')
8328
8329 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8330 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8331 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8332
8333 for _ in range(20):
8334 output = resolvectl('domain', 'veth99')
8335 if common or ipv4 or ipv6:
8336 if 'example.com' in output:
8337 break
8338 else:
8339 if 'example.com' not in output:
8340 break
8341 time.sleep(0.5)
8342 else:
8343 print(output)
8344 print(read_link_state_file('veth99'))
8345 self.fail('unexpected domain setting in resolved...')
8346
8347 stop_dnsmasq()
8348 remove_networkd_conf_dropin('default_use_domains.conf')
8349
8350 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8351 check(self, True, False, False)
8352 check(self, False, True, True)
8353 check(self, False, True, False)
8354 check(self, False, False, True)
8355 check(self, False, False, False)
8356
8357 def test_dhcp_client_use_dnr(self):
8358 def check(self, ipv4, ipv6, needs_reconfigure=False):
8359 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8360 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8361 f.write('[DHCPv4]\nUseDNS=')
8362 f.write('yes' if ipv4 else 'no')
8363 f.write('\n[DHCPv6]\nUseDNS=')
8364 f.write('yes' if ipv6 else 'no')
8365 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
8366
8367 networkctl_reload()
8368 if needs_reconfigure:
8369 networkctl_reconfigure('veth99')
8370 self.wait_online('veth99:routable')
8371
8372 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8373 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8374 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8375
8376 # make resolved re-read the link state file
8377 resolvectl('revert', 'veth99')
8378
8379 output = resolvectl('dns', 'veth99')
8380 print(output)
8381 if ipv4:
8382 self.assertIn('8.8.8.8#dns.google', output)
8383 self.assertIn('0.7.4.2#homer.simpson', output)
8384 else:
8385 self.assertNotIn('8.8.8.8#dns.google', output)
8386 if ipv6:
8387 self.assertIn('2001:4860:4860::8888#dns.google', output)
8388 else:
8389 self.assertNotIn('2001:4860:4860::8888#dns.google', output)
8390
8391 check_json(networkctl_json())
8392
8393 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8394
8395 start_networkd()
8396 self.wait_online('veth-peer:carrier')
8397 dnr_v4 = dnr_v4_instance_data(adn = "dns.google", addrs = ["8.8.8.8", "8.8.4.4"])
8398 dnr_v4 += dnr_v4_instance_data(adn = "homer.simpson", addrs = ["0.7.4.2"], alpns = ("dot","h2","h3"), dohpath = "/springfield{?dns}")
8399 dnr_v6 = dnr_v6_instance_data(adn = "dns.google", addrs = ["2001:4860:4860::8888", "2001:4860:4860::8844"])
8400 masq = lambda bs: ':'.join(f"{b:02x}" for b in bs)
8401 start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}',
8402 f'--dhcp-option=option6:144,{masq(dnr_v6)}')
8403
8404 check(self, True, True)
8405 check(self, True, False)
8406 check(self, False, True, needs_reconfigure=True)
8407 check(self, False, False)
8408
8409 def test_dhcp_client_use_captive_portal(self):
8410 def check(self, ipv4, ipv6, needs_reconfigure=False):
8411 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8412 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8413 f.write('[DHCPv4]\nUseCaptivePortal=')
8414 f.write('yes' if ipv4 else 'no')
8415 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8416 f.write('yes' if ipv6 else 'no')
8417 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8418
8419 networkctl_reload()
8420 if needs_reconfigure:
8421 networkctl_reconfigure('veth99')
8422 self.wait_online('veth99:routable')
8423
8424 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8425 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8426 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8427
8428 output = networkctl_status('veth99')
8429 print(output)
8430 if ipv4 or ipv6:
8431 self.assertIn('Captive Portal: http://systemd.io', output)
8432 else:
8433 self.assertNotIn('Captive Portal: http://systemd.io', output)
8434
8435 check_json(networkctl_json())
8436
8437 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8438
8439 start_networkd()
8440 self.wait_online('veth-peer:carrier')
8441 start_dnsmasq('--dhcp-option=114,http://systemd.io',
8442 '--dhcp-option=option6:103,http://systemd.io')
8443
8444 check(self, True, True)
8445 check(self, True, False)
8446 check(self, False, True, needs_reconfigure=True)
8447 check(self, False, False)
8448
8449 def test_dhcp_client_reject_captive_portal(self):
8450 def check(self, ipv4, ipv6):
8451 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
8452 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
8453 f.write('[DHCPv4]\nUseCaptivePortal=')
8454 f.write('yes' if ipv4 else 'no')
8455 f.write('\n[DHCPv6]\nUseCaptivePortal=')
8456 f.write('yes' if ipv6 else 'no')
8457 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
8458
8459 networkctl_reload()
8460 self.wait_online('veth99:routable')
8461
8462 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8463 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
8464 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
8465
8466 output = networkctl_status('veth99')
8467 print(output)
8468 self.assertNotIn('Captive Portal: ', output)
8469 self.assertNotIn('invalid/url', output)
8470
8471 check_json(networkctl_json())
8472
8473 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
8474
8475 start_networkd()
8476 self.wait_online('veth-peer:carrier')
8477 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
8478 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
8479 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
8480
8481 check(self, True, True)
8482 check(self, True, False)
8483 check(self, False, True)
8484 check(self, False, False)
8485
8486 class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
8487
8488 def setUp(self):
8489 setup_common()
8490
8491 def tearDown(self):
8492 tear_down_common()
8493
8494 def check_dhcp6_prefix(self, link):
8495 description = get_link_description(link)
8496
8497 self.assertIn('DHCPv6Client', description.keys())
8498 self.assertIn('Prefixes', description['DHCPv6Client'])
8499
8500 prefixInfo = description['DHCPv6Client']['Prefixes']
8501
8502 self.assertEqual(len(prefixInfo), 1)
8503
8504 self.assertIn('Prefix', prefixInfo[0].keys())
8505 self.assertIn('PrefixLength', prefixInfo[0].keys())
8506 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
8507 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
8508
8509 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
8510 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
8511 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
8512 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
8513
8514 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8515 def test_dhcp6pd_no_address(self):
8516 # For issue #29979.
8517 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
8518
8519 start_networkd()
8520 self.wait_online('veth-peer:routable')
8521 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
8522 self.wait_online('veth99:degraded')
8523
8524 print('### ip -6 address show dev veth99 scope global')
8525 output = check_output('ip -6 address show dev veth99 scope global')
8526 print(output)
8527 self.assertNotIn('inet6 3ffe:501:ffff', output)
8528
8529 print('### ip -6 route show dev lo')
8530 output = check_output('ip -6 route show dev lo')
8531 print(output)
8532 self.assertNotRegex(output, '3ffe:501:ffff:[2-9a-f]00::/56')
8533
8534 self.check_dhcp6_prefix('veth99')
8535
8536 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8537 def test_dhcp6pd_no_assign(self):
8538 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
8539 # However, the server does not provide IA_NA. For issue #31349.
8540 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
8541
8542 start_networkd()
8543 self.wait_online('veth-peer:routable')
8544 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
8545 self.wait_online('veth99:degraded')
8546
8547 print('### ip -6 address show dev veth99 scope global')
8548 output = check_output('ip -6 address show dev veth99 scope global')
8549 print(output)
8550 self.assertNotIn('inet6 3ffe:501:ffff', output)
8551
8552 print('### ip -6 route show type blackhole')
8553 output = check_output('ip -6 route show type blackhole')
8554 print(output)
8555 self.assertRegex(output, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8556
8557 self.check_dhcp6_prefix('veth99')
8558
8559 @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available")
8560 def test_dhcp6pd(self):
8561 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
8562 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8563 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8564 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8565 '25-dhcp-pd-downstream-dummy97.network',
8566 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8567 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
8568
8569 start_networkd()
8570 self.wait_online('veth-peer:routable')
8571 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
8572 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8573 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
8574
8575 self.setup_nftset('addr6', 'ipv6_addr')
8576 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
8577 self.setup_nftset('ifindex', 'iface_index')
8578
8579 # Check DBus assigned prefix information to veth99
8580 self.check_dhcp6_prefix('veth99')
8581
8582 print('### ip -6 address show dev veth-peer scope global')
8583 output = check_output('ip -6 address show dev veth-peer scope global')
8584 print(output)
8585 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
8586
8587 # Link Subnet IDs
8588 # test1: 0x00
8589 # dummy97: 0x01 (The link will appear later)
8590 # dummy98: 0x00
8591 # dummy99: auto -> 0x02 (No address assignment)
8592 # veth97: 0x08
8593 # veth98: 0x09
8594 # veth99: 0x10
8595
8596 print('### ip -6 address show dev veth99 scope global')
8597 output = check_output('ip -6 address show dev veth99 scope global')
8598 print(output)
8599 # IA_NA
8600 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
8601 # address in IA_PD (Token=static)
8602 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
8603 # address in IA_PD (Token=eui64)
8604 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
8605 # address in IA_PD (temporary)
8606 # Note that the temporary addresses may appear after the link enters configured state
8607 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')
8608
8609 print('### ip -6 address show dev test1 scope global')
8610 output = check_output('ip -6 address show dev test1 scope global')
8611 print(output)
8612 # address in IA_PD (Token=static)
8613 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8614 # address in IA_PD (temporary)
8615 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')
8616
8617 print('### ip -6 address show dev dummy98 scope global')
8618 output = check_output('ip -6 address show dev dummy98 scope global')
8619 print(output)
8620 # address in IA_PD (Token=static)
8621 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8622 # address in IA_PD (temporary)
8623 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')
8624
8625 print('### ip -6 address show dev dummy99 scope global')
8626 output = check_output('ip -6 address show dev dummy99 scope global')
8627 print(output)
8628 # Assign=no
8629 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
8630
8631 print('### ip -6 address show dev veth97 scope global')
8632 output = check_output('ip -6 address show dev veth97 scope global')
8633 print(output)
8634 # address in IA_PD (Token=static)
8635 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8636 # address in IA_PD (Token=eui64)
8637 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8638 # address in IA_PD (temporary)
8639 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')
8640
8641 print('### ip -6 address show dev veth97-peer scope global')
8642 output = check_output('ip -6 address show dev veth97-peer scope global')
8643 print(output)
8644 # NDisc address (Token=static)
8645 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8646 # NDisc address (Token=eui64)
8647 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8648 # NDisc address (temporary)
8649 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')
8650
8651 print('### ip -6 address show dev veth98 scope global')
8652 output = check_output('ip -6 address show dev veth98 scope global')
8653 print(output)
8654 # address in IA_PD (Token=static)
8655 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8656 # address in IA_PD (Token=eui64)
8657 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8658 # address in IA_PD (temporary)
8659 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')
8660
8661 print('### ip -6 address show dev veth98-peer scope global')
8662 output = check_output('ip -6 address show dev veth98-peer scope global')
8663 print(output)
8664 # NDisc address (Token=static)
8665 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8666 # NDisc address (Token=eui64)
8667 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8668 # NDisc address (temporary)
8669 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')
8670
8671 print('### ip -6 route show type unreachable')
8672 output = check_output('ip -6 route show type unreachable')
8673 print(output)
8674 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
8675
8676 print('### ip -6 route show dev veth99')
8677 output = check_output('ip -6 route show dev veth99')
8678 print(output)
8679 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
8680
8681 print('### ip -6 route show dev test1')
8682 output = check_output('ip -6 route show dev test1')
8683 print(output)
8684 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8685
8686 print('### ip -6 route show dev dummy98')
8687 output = check_output('ip -6 route show dev dummy98')
8688 print(output)
8689 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8690
8691 print('### ip -6 route show dev dummy99')
8692 output = check_output('ip -6 route show dev dummy99')
8693 print(output)
8694 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
8695
8696 print('### ip -6 route show dev veth97')
8697 output = check_output('ip -6 route show dev veth97')
8698 print(output)
8699 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
8700
8701 print('### ip -6 route show dev veth97-peer')
8702 output = check_output('ip -6 route show dev veth97-peer')
8703 print(output)
8704 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
8705
8706 print('### ip -6 route show dev veth98')
8707 output = check_output('ip -6 route show dev veth98')
8708 print(output)
8709 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
8710
8711 print('### ip -6 route show dev veth98-peer')
8712 output = check_output('ip -6 route show dev veth98-peer')
8713 print(output)
8714 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
8715
8716 # Test case for a downstream which appears later
8717 check_output('ip link add dummy97 type dummy')
8718 self.wait_online('dummy97:routable')
8719
8720 print('### ip -6 address show dev dummy97 scope global')
8721 output = check_output('ip -6 address show dev dummy97 scope global')
8722 print(output)
8723 # address in IA_PD (Token=static)
8724 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8725 # address in IA_PD (temporary)
8726 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')
8727
8728 print('### ip -6 route show dev dummy97')
8729 output = check_output('ip -6 route show dev dummy97')
8730 print(output)
8731 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
8732
8733 # Test case for reconfigure
8734 networkctl_reconfigure('dummy98', 'dummy99')
8735 self.wait_online('dummy98:routable', 'dummy99:degraded')
8736
8737 print('### ip -6 address show dev dummy98 scope global')
8738 output = check_output('ip -6 address show dev dummy98 scope global')
8739 print(output)
8740 # address in IA_PD (Token=static)
8741 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8742 # address in IA_PD (temporary)
8743 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')
8744
8745 print('### ip -6 address show dev dummy99 scope global')
8746 output = check_output('ip -6 address show dev dummy99 scope global')
8747 print(output)
8748 # Assign=no
8749 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
8750
8751 print('### ip -6 route show dev dummy98')
8752 output = check_output('ip -6 route show dev dummy98')
8753 print(output)
8754 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
8755
8756 print('### ip -6 route show dev dummy99')
8757 output = check_output('ip -6 route show dev dummy99')
8758 print(output)
8759 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
8760
8761 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
8762
8763 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
8764 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
8765 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
8766 self.check_nftset('ifindex', 'dummy98')
8767
8768 self.teardown_nftset('addr6', 'network6', 'ifindex')
8769
8770 def verify_dhcp4_6rd(self, tunnel_name, address_prefix, border_router):
8771 print('### ip -4 address show dev veth-peer scope global')
8772 output = check_output('ip -4 address show dev veth-peer scope global')
8773 print(output)
8774 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
8775
8776 # Link Subnet IDs
8777 # test1: 0x00
8778 # dummy97: 0x01 (The link will appear later)
8779 # dummy98: 0x00
8780 # dummy99: auto -> 0x0[23] (No address assignment)
8781 # 6rd-XXX: auto -> 0x0[23]
8782 # veth97: 0x08
8783 # veth98: 0x09
8784 # veth99: 0x10
8785
8786 print('### ip -4 address show dev veth99 scope global')
8787 output = check_output('ip -4 address show dev veth99 scope global')
8788 print(output)
8789 self.assertRegex(output, fr'inet {address_prefix}[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
8790
8791 print('### ip -6 address show dev veth99 scope global')
8792 output = check_output('ip -6 address show dev veth99 scope global')
8793 print(output)
8794 # address in IA_PD (Token=static)
8795 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8796 # address in IA_PD (Token=eui64)
8797 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
8798 # address in IA_PD (temporary)
8799 # Note that the temporary addresses may appear after the link enters configured state
8800 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')
8801
8802 print('### ip -6 address show dev test1 scope global')
8803 output = check_output('ip -6 address show dev test1 scope global')
8804 print(output)
8805 # address in IA_PD (Token=static)
8806 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8807 # address in IA_PD (temporary)
8808 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')
8809
8810 print('### ip -6 address show dev dummy98 scope global')
8811 output = check_output('ip -6 address show dev dummy98 scope global')
8812 print(output)
8813 # address in IA_PD (Token=static)
8814 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8815 # address in IA_PD (temporary)
8816 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')
8817
8818 print('### ip -6 address show dev dummy99 scope global')
8819 output = check_output('ip -6 address show dev dummy99 scope global')
8820 print(output)
8821 # Assign=no
8822 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
8823
8824 print('### ip -6 address show dev veth97 scope global')
8825 output = check_output('ip -6 address show dev veth97 scope global')
8826 print(output)
8827 # address in IA_PD (Token=static)
8828 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8829 # address in IA_PD (Token=eui64)
8830 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
8831 # address in IA_PD (temporary)
8832 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')
8833
8834 print('### ip -6 address show dev veth97-peer scope global')
8835 output = check_output('ip -6 address show dev veth97-peer scope global')
8836 print(output)
8837 # NDisc address (Token=static)
8838 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8839 # NDisc address (Token=eui64)
8840 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8841 # NDisc address (temporary)
8842 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')
8843
8844 print('### ip -6 address show dev veth98 scope global')
8845 output = check_output('ip -6 address show dev veth98 scope global')
8846 print(output)
8847 # address in IA_PD (Token=static)
8848 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8849 # address in IA_PD (Token=eui64)
8850 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
8851 # address in IA_PD (temporary)
8852 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')
8853
8854 print('### ip -6 address show dev veth98-peer scope global')
8855 output = check_output('ip -6 address show dev veth98-peer scope global')
8856 print(output)
8857 # NDisc address (Token=static)
8858 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
8859 # NDisc address (Token=eui64)
8860 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
8861 # NDisc address (temporary)
8862 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')
8863
8864 print('### ip -6 route show type unreachable')
8865 output = check_output('ip -6 route show type unreachable')
8866 print(output)
8867 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
8868
8869 print('### ip -6 route show dev veth99')
8870 output = check_output('ip -6 route show dev veth99')
8871 print(output)
8872 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
8873
8874 print('### ip -6 route show dev test1')
8875 output = check_output('ip -6 route show dev test1')
8876 print(output)
8877 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
8878
8879 print('### ip -6 route show dev dummy98')
8880 output = check_output('ip -6 route show dev dummy98')
8881 print(output)
8882 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
8883
8884 print('### ip -6 route show dev dummy99')
8885 output = check_output('ip -6 route show dev dummy99')
8886 print(output)
8887 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
8888
8889 print('### ip -6 route show dev veth97')
8890 output = check_output('ip -6 route show dev veth97')
8891 print(output)
8892 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
8893
8894 print('### ip -6 route show dev veth97-peer')
8895 output = check_output('ip -6 route show dev veth97-peer')
8896 print(output)
8897 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
8898
8899 print('### ip -6 route show dev veth98')
8900 output = check_output('ip -6 route show dev veth98')
8901 print(output)
8902 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
8903
8904 print('### ip -6 route show dev veth98-peer')
8905 output = check_output('ip -6 route show dev veth98-peer')
8906 print(output)
8907 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
8908
8909 print('### ip -6 address show dev dummy97 scope global')
8910 output = check_output('ip -6 address show dev dummy97 scope global')
8911 print(output)
8912 # address in IA_PD (Token=static)
8913 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
8914 # address in IA_PD (temporary)
8915 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')
8916
8917 print('### ip -6 route show dev dummy97')
8918 output = check_output('ip -6 route show dev dummy97')
8919 print(output)
8920 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
8921
8922 print(f'### ip -d link show dev {tunnel_name}')
8923 output = check_output(f'ip -d link show dev {tunnel_name}')
8924 print(output)
8925 self.assertIn(f'link/sit {address_prefix}', output)
8926 self.assertIn(f'local {address_prefix}', output)
8927 self.assertIn('ttl 64', output)
8928 self.assertIn('6rd-prefix 2001:db8::/32', output)
8929 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
8930
8931 print(f'### ip -6 address show dev {tunnel_name}')
8932 output = check_output(f'ip -6 address show dev {tunnel_name}')
8933 print(output)
8934 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')
8935 self.assertRegex(output, fr'inet6 ::{address_prefix}[0-9]+/96 scope global')
8936
8937 print(f'### ip -6 route show dev {tunnel_name}')
8938 output = check_output(f'ip -6 route show dev {tunnel_name}')
8939 print(output)
8940 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
8941 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
8942
8943 print('### ip -6 route show default')
8944 output = check_output('ip -6 route show default')
8945 print(output)
8946 self.assertIn('default', output)
8947 self.assertIn(f'via ::{border_router} dev {tunnel_name}', output)
8948
8949 def test_dhcp4_6rd(self):
8950 def get_dhcp_6rd_prefix(link):
8951 description = get_link_description(link)
8952
8953 self.assertIn('DHCPv4Client', description.keys())
8954 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
8955
8956 prefixInfo = description['DHCPv4Client']['6rdPrefix']
8957 self.assertIn('Prefix', prefixInfo.keys())
8958 self.assertIn('PrefixLength', prefixInfo.keys())
8959 self.assertIn('IPv4MaskLength', prefixInfo.keys())
8960 self.assertIn('BorderRouters', prefixInfo.keys())
8961
8962 return prefixInfo
8963
8964 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
8965 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
8966 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
8967 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
8968 '25-dhcp-pd-downstream-dummy97.network',
8969 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
8970 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
8971 '80-6rd-tunnel.network')
8972
8973 start_networkd()
8974 self.wait_online('veth-peer:routable')
8975
8976 # ipv4masklen: 8
8977 # 6rd-prefix: 2001:db8::/32
8978 # br-addresss: 10.0.0.1
8979
8980 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',
8981 ipv4_range='10.100.100.100,10.100.100.200',
8982 ipv4_router='10.0.0.1')
8983 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
8984 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
8985
8986 # Check the DBus interface for assigned prefix information
8987 prefixInfo = get_dhcp_6rd_prefix('veth99')
8988
8989 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
8990 self.assertEqual(prefixInfo['PrefixLength'], 32)
8991 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
8992 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
8993
8994 # Test case for a downstream which appears later
8995 check_output('ip link add dummy97 type dummy')
8996 self.wait_online('dummy97:routable')
8997
8998 # Find tunnel name
8999 tunnel_name = None
9000 for name in os.listdir('/sys/class/net/'):
9001 if name.startswith('6rd-'):
9002 tunnel_name = name
9003 break
9004
9005 self.wait_online(f'{tunnel_name}:routable')
9006
9007 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
9008
9009 # Test case for reconfigure
9010 networkctl_reconfigure('dummy98', 'dummy99')
9011 self.wait_online('dummy98:routable', 'dummy99:degraded')
9012
9013 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '10.0.0.1')
9014
9015 # Change the address range and (border) router, then if check the same tunnel is reused.
9016 stop_dnsmasq()
9017 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',
9018 ipv4_range='10.100.100.200,10.100.100.250',
9019 ipv4_router='10.0.0.2')
9020
9021 print('Wait for the DHCP lease to be renewed/rebind')
9022 time.sleep(120)
9023
9024 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
9025 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
9026
9027 self.verify_dhcp4_6rd(tunnel_name, '10.100.100.2', '10.0.0.2')
9028
9029 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
9030
9031 def setUp(self):
9032 setup_common()
9033
9034 def tearDown(self):
9035 tear_down_common()
9036
9037 def test_ipv6_route_prefix(self):
9038 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
9039 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9040
9041 start_networkd()
9042 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9043
9044 print('### ip -6 address show dev veth-peer')
9045 output = check_output('ip -6 address show dev veth-peer')
9046 print(output)
9047 self.assertIn('inet6 2001:db8:0:1:', output)
9048 self.assertNotIn('inet6 2001:db8:0:2:', output)
9049 self.assertNotIn('inet6 2001:db8:0:3:', output)
9050
9051 print('### ip -6 route show dev veth-peer')
9052 output = check_output('ip -6 route show dev veth-peer')
9053 print(output)
9054 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9055 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
9056 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
9057 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
9058 self.assertNotIn('2001:db1:fff::/64', output)
9059 self.assertNotIn('2001:db2:fff::/64', output)
9060
9061 print('### ip -6 nexthop show dev veth-peer')
9062 output = check_output('ip -6 nexthop show dev veth-peer')
9063 print(output)
9064 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9065
9066 print('### ip -6 address show dev veth99')
9067 output = check_output('ip -6 address show dev veth99')
9068 print(output)
9069 self.assertNotIn('inet6 2001:db8:0:1:', output)
9070 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
9071 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
9072 self.assertNotIn('inet6 2001:db8:0:3:', output)
9073
9074 output = resolvectl('dns', 'veth-peer')
9075 print(output)
9076 self.assertRegex(output, '2001:db8:1:1::2')
9077
9078 output = resolvectl('domain', 'veth-peer')
9079 print(output)
9080 self.assertIn('example.com', output)
9081
9082 check_json(networkctl_json())
9083
9084 output = networkctl_json('veth-peer')
9085 check_json(output)
9086
9087 # PREF64 or NAT64
9088 pref64 = json.loads(output)['NDisc']['PREF64'][0]
9089
9090 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
9091 self.assertEqual(prefix, '64:ff9b::')
9092
9093 prefix_length = pref64['PrefixLength']
9094 self.assertEqual(prefix_length, 96)
9095
9096 def test_ipv6_route_prefix_deny_list(self):
9097 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
9098 '12-dummy.netdev', '25-ipv6ra-uplink.network')
9099
9100 start_networkd()
9101 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
9102
9103 print('### ip -6 address show dev veth-peer')
9104 output = check_output('ip -6 address show dev veth-peer')
9105 print(output)
9106 self.assertIn('inet6 2001:db8:0:1:', output)
9107 self.assertNotIn('inet6 2001:db8:0:2:', output)
9108
9109 print('### ip -6 route show dev veth-peer')
9110 output = check_output('ip -6 route show dev veth-peer')
9111 print(output)
9112 self.assertIn('2001:db8:0:1::/64 proto ra', output)
9113 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
9114 self.assertRegex(output, r'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc')
9115 self.assertNotIn('2001:db1:fff::/64', output)
9116
9117 print('### ip -6 nexthop show dev veth-peer')
9118 output = check_output('ip -6 nexthop show dev veth-peer')
9119 print(output)
9120 self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra')
9121
9122 print('### ip -6 address show dev veth99')
9123 output = check_output('ip -6 address show dev veth99')
9124 print(output)
9125 self.assertNotIn('inet6 2001:db8:0:1:', output)
9126 self.assertIn('inet6 2001:db8:0:2:', output)
9127
9128 output = resolvectl('dns', 'veth-peer')
9129 print(output)
9130 self.assertRegex(output, '2001:db8:1:1::2')
9131
9132 output = resolvectl('domain', 'veth-peer')
9133 print(output)
9134 self.assertIn('example.com', output)
9135
9136 class NetworkdMTUTests(unittest.TestCase, Utilities):
9137
9138 def setUp(self):
9139 setup_common()
9140
9141 def tearDown(self):
9142 tear_down_common()
9143
9144 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
9145 if not ipv6_mtu:
9146 ipv6_mtu = mtu
9147
9148 # test normal start
9149 start_networkd()
9150 self.wait_online('dummy98:routable')
9151 self.check_link_attr('dummy98', 'mtu', mtu)
9152 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
9153
9154 # test normal restart
9155 restart_networkd()
9156 self.wait_online('dummy98:routable')
9157 self.check_link_attr('dummy98', 'mtu', mtu)
9158 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
9159
9160 if reset:
9161 self.reset_check_mtu(mtu, ipv6_mtu)
9162
9163 def reset_check_mtu(self, mtu, ipv6_mtu=None):
9164 ''' test setting mtu/ipv6_mtu with interface already up '''
9165 stop_networkd()
9166
9167 # note - changing the device mtu resets the ipv6 mtu
9168 check_output('ip link set up mtu 1501 dev dummy98')
9169 check_output('ip link set up mtu 1500 dev dummy98')
9170 self.check_link_attr('dummy98', 'mtu', '1500')
9171 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
9172
9173 self.check_mtu(mtu, ipv6_mtu, reset=False)
9174
9175 def test_mtu_network(self):
9176 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
9177 self.check_mtu('1600')
9178
9179 def test_mtu_netdev(self):
9180 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
9181 # note - MTU set by .netdev happens ONLY at device creation!
9182 self.check_mtu('1600', reset=False)
9183
9184 def test_mtu_link(self):
9185 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
9186 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
9187 self.check_mtu('1600', reset=False)
9188
9189 def test_ipv6_mtu(self):
9190 ''' set ipv6 mtu without setting device mtu '''
9191 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
9192 self.check_mtu('1500', '1400')
9193
9194 def test_ipv6_mtu_toolarge(self):
9195 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
9196 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
9197 self.check_mtu('1500', '1500')
9198
9199 def test_mtu_network_ipv6_mtu(self):
9200 ''' set ipv6 mtu and set device mtu via network file '''
9201 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
9202 self.check_mtu('1600', '1550')
9203
9204 def test_mtu_netdev_ipv6_mtu(self):
9205 ''' set ipv6 mtu and set device mtu via netdev file '''
9206 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
9207 self.check_mtu('1600', '1550', reset=False)
9208
9209 def test_mtu_link_ipv6_mtu(self):
9210 ''' set ipv6 mtu and set device mtu via link file '''
9211 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
9212 self.check_mtu('1600', '1550', reset=False)
9213
9214 class NetworkdSysctlTest(unittest.TestCase, Utilities):
9215
9216 def setUp(self):
9217 setup_common()
9218
9219 def tearDown(self):
9220 tear_down_common()
9221
9222 @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")
9223 def check_sysctl_watch(self):
9224 copy_network_unit('12-dummy.network', '12-dummy.netdev', '12-dummy.link')
9225 start_networkd()
9226
9227 self.wait_online('dummy98:routable')
9228
9229 # Change managed sysctls
9230 call('sysctl -w net.ipv6.conf.dummy98.accept_ra=1')
9231 call('sysctl -w net.ipv6.conf.dummy98.mtu=1360')
9232 call('sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0')
9233 call('sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1')
9234
9235 # And unmanaged ones
9236 call('sysctl -w net.ipv6.conf.dummy98.hop_limit=4')
9237 call('sysctl -w net.ipv6.conf.dummy98.max_addresses=10')
9238
9239 log=read_networkd_log()
9240 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'")
9241 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'")
9242 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'")
9243 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'")
9244 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'", log)
9245 self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log)
9246
9247 if __name__ == '__main__':
9248 parser = argparse.ArgumentParser()
9249 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
9250 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
9251 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
9252 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
9253 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
9254 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
9255 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
9256 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)
9257 parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false')
9258 ns, unknown_args = parser.parse_known_args(namespace=unittest)
9259
9260 if ns.build_dir:
9261 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
9262 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
9263 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
9264 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
9265 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
9266 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
9267 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
9268 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
9269 build_dir = ns.build_dir
9270
9271 if ns.source_dir:
9272 source_dir = ns.source_dir
9273 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
9274 elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))):
9275 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
9276 else:
9277 source_dir = None
9278
9279 if networkd_bin is None or resolved_bin is None or timesyncd_bin is None:
9280 print("networkd tests require networkd/resolved/timesyncd to be enabled")
9281 sys.exit(77)
9282
9283 use_valgrind = ns.use_valgrind
9284 enable_debug = ns.enable_debug
9285 asan_options = ns.asan_options
9286 lsan_options = ns.lsan_options
9287 ubsan_options = ns.ubsan_options
9288 with_coverage = ns.with_coverage or "COVERAGE_BUILD_DIR" in os.environ
9289 show_journal = ns.show_journal
9290
9291 if use_valgrind:
9292 # Do not forget the trailing space.
9293 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
9294
9295 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
9296 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
9297 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
9298 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
9299 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
9300
9301 if build_dir:
9302 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
9303 else:
9304 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
9305
9306 if asan_options:
9307 env.update({'ASAN_OPTIONS': asan_options})
9308 if lsan_options:
9309 env.update({'LSAN_OPTIONS': lsan_options})
9310 if ubsan_options:
9311 env.update({'UBSAN_OPTIONS': ubsan_options})
9312 if use_valgrind:
9313 env.update({'SYSTEMD_MEMPOOL': '0'})
9314
9315 wait_online_env = env.copy()
9316 if enable_debug:
9317 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
9318
9319 unittest.main(
9320 verbosity=3,
9321 argv=[
9322 sys.argv[0],
9323 *unknown_args,
9324 *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else [])
9325 ],
9326 )