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