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