]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
4c69bc903366efa8492b706ec0d9aea439985f72
[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::1 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5555 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5556 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 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 2002:da8:1::2 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
5560 check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::3 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
5561 self.wait_route_dropped('veth99', r'2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
5562 self.wait_route_dropped('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 proto redirect', ipv='-6', timeout_sec=10)
5563 self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d via 2002:da8:1::2 proto redirect', ipv='-6', timeout_sec=10)
5564 self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::3 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 redirect', ipv='-6', timeout_sec=10)
5573 self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
5574
5575 def test_ipv6_token_prefixstable(self):
5576 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5577 start_networkd()
5578 self.wait_online('veth99:routable', 'veth-peer:degraded')
5579
5580 output = networkctl_status('veth99')
5581 print(output)
5582 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
5583 self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
5584
5585 def test_ipv6_token_prefixstable_without_address(self):
5586 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5587 start_networkd()
5588 self.wait_online('veth99:routable', 'veth-peer:degraded')
5589
5590 output = networkctl_status('veth99')
5591 print(output)
5592 self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
5593 self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
5594
5595 def check_router_hop_limit(self, hop_limit):
5596 self.wait_route('client', rf'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv='-6', timeout_sec=10)
5597
5598 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5599 print(output)
5600 self.assertIn(f'hoplimit {hop_limit}', output)
5601
5602 self.check_ipv6_sysctl_attr('client', 'hop_limit', f'{hop_limit}')
5603
5604 def test_router_hop_limit(self):
5605 copy_network_unit('25-veth-client.netdev',
5606 '25-veth-router.netdev',
5607 '26-bridge.netdev',
5608 '25-veth-bridge.network',
5609 '25-veth-client.network',
5610 '25-veth-router-hop-limit.network',
5611 '25-bridge99.network')
5612 start_networkd()
5613 self.wait_online('client-p:enslaved',
5614 'router:degraded', 'router-p:enslaved',
5615 'bridge99:routable')
5616
5617 self.check_router_hop_limit(42)
5618
5619 with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
5620 f.write('\n[IPv6SendRA]\nHopLimit=43\n')
5621
5622 networkctl_reload()
5623
5624 self.check_router_hop_limit(43)
5625
5626 def test_router_preference(self):
5627 copy_network_unit('25-veth-client.netdev',
5628 '25-veth-router-high.netdev',
5629 '25-veth-router-low.netdev',
5630 '26-bridge.netdev',
5631 '25-veth-bridge.network',
5632 '25-veth-client.network',
5633 '25-veth-router-high.network',
5634 '25-veth-router-low.network',
5635 '25-bridge99.network')
5636 start_networkd()
5637 self.wait_online('client-p:enslaved',
5638 'router-high:degraded', 'router-high-p:enslaved',
5639 'router-low:degraded', 'router-low-p:enslaved',
5640 'bridge99:routable')
5641
5642 networkctl_reconfigure('client')
5643 self.wait_online('client:routable')
5644
5645 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5646 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5647 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv='-6', timeout_sec=10)
5648 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv='-6', timeout_sec=10)
5649
5650 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5651 print(output)
5652 self.assertIn('metric 512', output)
5653 self.assertIn('pref high', output)
5654 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5655 print(output)
5656 self.assertIn('metric 2048', output)
5657 self.assertIn('pref low', output)
5658
5659 with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
5660 f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5661
5662 networkctl_reload()
5663 self.wait_online('client:routable')
5664
5665 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5666 self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
5667 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv='-6', timeout_sec=10)
5668 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv='-6', timeout_sec=10)
5669
5670 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5671 print(output)
5672 self.assertIn('metric 100', output)
5673 self.assertNotIn('metric 512', output)
5674 self.assertIn('pref high', output)
5675 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5676 print(output)
5677 self.assertIn('metric 300', output)
5678 self.assertNotIn('metric 2048', output)
5679 self.assertIn('pref low', output)
5680
5681 # swap the preference (for issue #28439)
5682 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5683 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5684 networkctl_reload()
5685 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv='-6', timeout_sec=10)
5686 self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv='-6', timeout_sec=10)
5687
5688 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5689 print(output)
5690 self.assertIn('metric 300', output)
5691 self.assertNotIn('metric 100', output)
5692 self.assertIn('pref low', output)
5693 self.assertNotIn('pref high', output)
5694 output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5695 print(output)
5696 self.assertIn('metric 100', output)
5697 self.assertNotIn('metric 300', output)
5698 self.assertIn('pref high', output)
5699 self.assertNotIn('pref low', output)
5700
5701 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5702 def test_captive_portal(self):
5703 copy_network_unit('25-veth-client.netdev',
5704 '25-veth-router-captive.netdev',
5705 '26-bridge.netdev',
5706 '25-veth-client-captive.network',
5707 '25-veth-router-captive.network',
5708 '25-veth-bridge-captive.network',
5709 '25-bridge99.network')
5710 start_networkd()
5711 self.wait_online('bridge99:routable', 'client-p:enslaved',
5712 'router-captive:degraded', 'router-captivep:enslaved')
5713
5714 start_radvd(config_file='captive-portal.conf')
5715 networkctl_reconfigure('client')
5716 self.wait_online('client:routable')
5717
5718 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5719 output = networkctl_status('client')
5720 print(output)
5721 self.assertIn('Captive Portal: http://systemd.io', output)
5722
5723 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5724 def test_invalid_captive_portal(self):
5725 def radvd_write_config(captive_portal_uri):
5726 with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
5727 f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5728
5729 captive_portal_uris = [
5730 "42ěščěškd ěšč ě s",
5731 " ",
5732 "🤔",
5733 ]
5734
5735 copy_network_unit('25-veth-client.netdev',
5736 '25-veth-router-captive.netdev',
5737 '26-bridge.netdev',
5738 '25-veth-client-captive.network',
5739 '25-veth-router-captive.network',
5740 '25-veth-bridge-captive.network',
5741 '25-bridge99.network')
5742 start_networkd()
5743 self.wait_online('bridge99:routable', 'client-p:enslaved',
5744 'router-captive:degraded', 'router-captivep:enslaved')
5745
5746 for uri in captive_portal_uris:
5747 print(f"Captive portal: {uri}")
5748 radvd_write_config(uri)
5749 stop_radvd()
5750 start_radvd(config_file='bogus-captive-portal.conf')
5751 networkctl_reconfigure('client')
5752 self.wait_online('client:routable')
5753
5754 self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
5755 output = networkctl_status('client')
5756 print(output)
5757 self.assertNotIn('Captive Portal:', output)
5758
5759 class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
5760
5761 def setUp(self):
5762 setup_common()
5763
5764 def tearDown(self):
5765 tear_down_common()
5766
5767 def check_dhcp_server(self, persist_leases=True):
5768 output = networkctl_status('veth99')
5769 print(output)
5770 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5771 self.assertIn('Gateway: 192.168.5.3', output)
5772 self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
5773 self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
5774
5775 output = networkctl_status('veth-peer')
5776 print(output)
5777 self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
5778
5779 if persist_leases:
5780 with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
5781 check_json(f.read())
5782 else:
5783 self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
5784
5785 def test_dhcp_server(self):
5786 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5787 start_networkd()
5788 self.wait_online('veth99:routable', 'veth-peer:routable')
5789
5790 self.check_dhcp_server()
5791
5792 networkctl_reconfigure('veth-peer')
5793 self.wait_online('veth-peer:routable')
5794
5795 for _ in range(10):
5796 output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
5797 if 'Offered DHCP leases: 192.168.5.' in output:
5798 break
5799 time.sleep(.2)
5800 else:
5801 self.fail()
5802
5803 def test_dhcp_server_persist_leases_no(self):
5804 copy_networkd_conf_dropin('persist-leases-no.conf')
5805 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5806 start_networkd()
5807 self.wait_online('veth99:routable', 'veth-peer:routable')
5808
5809 self.check_dhcp_server(persist_leases=False)
5810
5811 remove_networkd_conf_dropin('persist-leases-no.conf')
5812 with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
5813 f.write('[DHCPServer]\nPersistLeases=no')
5814 restart_networkd()
5815 self.wait_online('veth99:routable', 'veth-peer:routable')
5816
5817 self.check_dhcp_server(persist_leases=False)
5818
5819 def test_dhcp_server_null_server_address(self):
5820 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5821 start_networkd()
5822 self.wait_online('veth99:routable', 'veth-peer:routable')
5823
5824 output = check_output('ip --json address show dev veth-peer')
5825 server_address = json.loads(output)[0]['addr_info'][0]['local']
5826 print(server_address)
5827
5828 output = check_output('ip --json address show dev veth99')
5829 client_address = json.loads(output)[0]['addr_info'][0]['local']
5830 print(client_address)
5831
5832 output = networkctl_status('veth99')
5833 print(output)
5834 self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
5835 self.assertIn(f'Gateway: {server_address}', output)
5836 self.assertIn(f'DNS: {server_address}', output)
5837 self.assertIn(f'NTP: {server_address}', output)
5838
5839 output = networkctl_status('veth-peer')
5840 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5841
5842 # Check if the same addresses are used even if the service is restarted.
5843 restart_networkd()
5844 self.wait_online('veth99:routable', 'veth-peer:routable')
5845
5846 output = check_output('ip -4 address show dev veth-peer')
5847 print(output)
5848 self.assertIn(f'{server_address}', output)
5849
5850 output = check_output('ip -4 address show dev veth99')
5851 print(output)
5852 self.assertIn(f'{client_address}', output)
5853
5854 output = networkctl_status('veth99')
5855 print(output)
5856 self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
5857 self.assertIn(f'Gateway: {server_address}', output)
5858 self.assertIn(f'DNS: {server_address}', output)
5859 self.assertIn(f'NTP: {server_address}', output)
5860
5861 output = networkctl_status('veth-peer')
5862 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5863
5864 def test_dhcp_server_with_uplink(self):
5865 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5866 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5867 start_networkd()
5868 self.wait_online('veth99:routable', 'veth-peer:routable')
5869
5870 output = networkctl_status('veth99')
5871 print(output)
5872 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5873 self.assertIn('Gateway: 192.168.5.3', output)
5874 self.assertIn('DNS: 192.168.5.1', output)
5875 self.assertIn('NTP: 192.168.5.1', output)
5876
5877 def test_emit_router_timezone(self):
5878 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5879 start_networkd()
5880 self.wait_online('veth99:routable', 'veth-peer:routable')
5881
5882 output = networkctl_status('veth99')
5883 print(output)
5884 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5885 self.assertIn('Gateway: 192.168.5.1', output)
5886 self.assertIn('Time Zone: Europe/Berlin', output)
5887
5888 def test_dhcp_server_static_lease(self):
5889 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5890 start_networkd()
5891 self.wait_online('veth99:routable', 'veth-peer:routable')
5892
5893 output = networkctl_status('veth99')
5894 print(output)
5895 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
5896 self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output)
5897
5898 def test_dhcp_server_static_lease_default_client_id(self):
5899 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5900 start_networkd()
5901 self.wait_online('veth99:routable', 'veth-peer:routable')
5902
5903 output = networkctl_status('veth99')
5904 print(output)
5905 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
5906 self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5907
5908 class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
5909
5910 def setUp(self):
5911 setup_common()
5912
5913 def tearDown(self):
5914 tear_down_common()
5915
5916 def test_relay_agent(self):
5917 copy_network_unit('25-agent-veth-client.netdev',
5918 '25-agent-veth-server.netdev',
5919 '25-agent-client.network',
5920 '25-agent-server.network',
5921 '25-agent-client-peer.network',
5922 '25-agent-server-peer.network')
5923 start_networkd()
5924
5925 self.wait_online('client:routable')
5926
5927 output = networkctl_status('client')
5928 print(output)
5929 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5930
5931 def test_relay_agent_on_bridge(self):
5932 copy_network_unit('25-agent-bridge.netdev',
5933 '25-agent-veth-client.netdev',
5934 '25-agent-bridge.network',
5935 '25-agent-bridge-port.network',
5936 '25-agent-client.network')
5937 start_networkd()
5938 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5939
5940 # For issue #30763.
5941 expect = 'bridge-relay: DHCPv4 server: STARTED'
5942 for _ in range(20):
5943 if expect in read_networkd_log():
5944 break
5945 time.sleep(0.5)
5946 else:
5947 self.fail()
5948
5949 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
5950
5951 def setUp(self):
5952 setup_common()
5953
5954 def tearDown(self):
5955 tear_down_common()
5956
5957 def test_dhcp_client_ipv6_only(self):
5958 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5959
5960 start_networkd()
5961 self.wait_online('veth-peer:carrier')
5962
5963 # information request mode
5964 # The name ipv6-only option may not be supported by older dnsmasq
5965 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5966 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5967 '--dhcp-option=option6:dns-server,[2600::ee]',
5968 '--dhcp-option=option6:ntp-server,[2600::ff]',
5969 ra_mode='ra-stateless')
5970 self.wait_online('veth99:routable', 'veth-peer:routable')
5971
5972 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5973 # Let's wait for the expected DNS server being listed in the state file.
5974 for _ in range(100):
5975 output = read_link_state_file('veth99')
5976 if 'DNS=2600::ee' in output:
5977 break
5978 time.sleep(.2)
5979
5980 # Check link state file
5981 print('## link state file')
5982 output = read_link_state_file('veth99')
5983 print(output)
5984 self.assertIn('DNS=2600::ee', output)
5985 self.assertIn('NTP=2600::ff', output)
5986
5987 # Check manager state file
5988 print('## manager state file')
5989 output = read_manager_state_file()
5990 print(output)
5991 self.assertRegex(output, 'DNS=.*2600::ee')
5992 self.assertRegex(output, 'NTP=.*2600::ff')
5993
5994 print('## dnsmasq log')
5995 output = read_dnsmasq_log_file()
5996 print(output)
5997 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
5998 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
5999 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6000 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6001 self.assertNotIn('DHCPREPLY(veth-peer)', output)
6002
6003 # Check json format
6004 check_json(networkctl_json('veth99'))
6005
6006 # solicit mode
6007 stop_dnsmasq()
6008 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6009 '--dhcp-option=option6:dns-server,[2600::ee]',
6010 '--dhcp-option=option6:ntp-server,[2600::ff]')
6011 networkctl_reconfigure('veth99')
6012 self.wait_online('veth99:routable', 'veth-peer:routable')
6013
6014 # checking address
6015 output = check_output('ip address show dev veth99 scope global')
6016 print(output)
6017 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6018 self.assertNotIn('192.168.5', output)
6019
6020 # checking semi-static route
6021 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6022 print(output)
6023 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6024
6025 # Confirm that ipv6 token is not set in the kernel
6026 output = check_output('ip token show dev veth99')
6027 print(output)
6028 self.assertRegex(output, 'token :: dev veth99')
6029
6030 # Make manager and link state file updated
6031 resolvectl('revert', 'veth99')
6032
6033 # Check link state file
6034 print('## link state file')
6035 output = read_link_state_file('veth99')
6036 print(output)
6037 self.assertIn('DNS=2600::ee', output)
6038 self.assertIn('NTP=2600::ff', output)
6039
6040 # Check manager state file
6041 print('## manager state file')
6042 output = read_manager_state_file()
6043 print(output)
6044 self.assertRegex(output, 'DNS=.*2600::ee')
6045 self.assertRegex(output, 'NTP=.*2600::ff')
6046
6047 print('## dnsmasq log')
6048 output = read_dnsmasq_log_file()
6049 print(output)
6050 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
6051 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6052 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6053 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6054 self.assertIn('DHCPREPLY(veth-peer)', output)
6055 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6056
6057 # Check json format
6058 check_json(networkctl_json('veth99'))
6059
6060 # Testing without rapid commit support
6061 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
6062 f.write('\n[DHCPv6]\nRapidCommit=no\n')
6063
6064 stop_dnsmasq()
6065 start_dnsmasq('--dhcp-option=108,00:00:02:00',
6066 '--dhcp-option=option6:dns-server,[2600::ee]',
6067 '--dhcp-option=option6:ntp-server,[2600::ff]')
6068
6069 networkctl_reload()
6070 self.wait_online('veth99:routable', 'veth-peer:routable')
6071
6072 # checking address
6073 output = check_output('ip address show dev veth99 scope global')
6074 print(output)
6075 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6076 self.assertNotIn('192.168.5', output)
6077
6078 # checking semi-static route
6079 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6080 print(output)
6081 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6082
6083 # Make manager and link state file updated
6084 resolvectl('revert', 'veth99')
6085
6086 # Check link state file
6087 print('## link state file')
6088 output = read_link_state_file('veth99')
6089 print(output)
6090 self.assertIn('DNS=2600::ee', output)
6091 self.assertIn('NTP=2600::ff', output)
6092
6093 # Check manager state file
6094 print('## manager state file')
6095 output = read_manager_state_file()
6096 print(output)
6097 self.assertRegex(output, 'DNS=.*2600::ee')
6098 self.assertRegex(output, 'NTP=.*2600::ff')
6099
6100 print('## dnsmasq log')
6101 output = read_dnsmasq_log_file()
6102 print(output)
6103 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
6104 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6105 self.assertIn('DHCPADVERTISE(veth-peer)', output)
6106 self.assertIn('DHCPREQUEST(veth-peer)', output)
6107 self.assertIn('DHCPREPLY(veth-peer)', output)
6108 self.assertNotIn('rapid-commit', output)
6109
6110 # Check json format
6111 check_json(networkctl_json('veth99'))
6112
6113 def test_dhcp_client_ipv6_dbus_status(self):
6114 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
6115 start_networkd()
6116 self.wait_online('veth-peer:carrier')
6117
6118 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
6119 # bit set) has yet been received and the configuration does not include WithoutRA=true
6120 state = get_dhcp6_client_state('veth99')
6121 print(f"DHCPv6 client state = {state}")
6122 self.assertEqual(state, 'stopped')
6123
6124 state = get_dhcp4_client_state('veth99')
6125 print(f"DHCPv4 client state = {state}")
6126 self.assertEqual(state, 'selecting')
6127
6128 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6129 self.wait_online('veth99:routable', 'veth-peer:routable')
6130
6131 state = get_dhcp6_client_state('veth99')
6132 print(f"DHCPv6 client state = {state}")
6133 self.assertEqual(state, 'bound')
6134
6135 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
6136 for _ in range(100):
6137 state = get_dhcp4_client_state('veth99')
6138 if state == 'stopped':
6139 break
6140 time.sleep(.2)
6141
6142 print(f"DHCPv4 client state = {state}")
6143 self.assertEqual(state, 'stopped')
6144
6145 # restart dnsmasq to clear log
6146 stop_dnsmasq()
6147 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6148
6149 # Test renew command
6150 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
6151 networkctl('renew', 'veth99')
6152
6153 for _ in range(100):
6154 state = get_dhcp4_client_state('veth99')
6155 if state == 'stopped':
6156 break
6157 time.sleep(.2)
6158
6159 print(f"DHCPv4 client state = {state}")
6160 self.assertEqual(state, 'stopped')
6161
6162 print('## dnsmasq log')
6163 output = read_dnsmasq_log_file()
6164 print(output)
6165 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
6166 self.assertIn('DHCPOFFER(veth-peer)', output)
6167 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6168 self.assertNotIn('DHCPACK(veth-peer)', output)
6169
6170 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
6171 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
6172
6173 start_networkd()
6174 self.wait_online('veth-peer:carrier')
6175 start_dnsmasq()
6176 self.wait_online('veth99:routable', 'veth-peer:routable')
6177
6178 # checking address
6179 output = check_output('ip address show dev veth99 scope global')
6180 print(output)
6181 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6182 self.assertNotIn('192.168.5', output)
6183
6184 print('## dnsmasq log')
6185 output = read_dnsmasq_log_file()
6186 print(output)
6187 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
6188 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6189 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6190 self.assertIn('DHCPREPLY(veth-peer)', output)
6191 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6192
6193 def test_dhcp_client_ipv4_only(self):
6194 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6195
6196 self.setup_nftset('addr4', 'ipv4_addr')
6197 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6198 self.setup_nftset('ifindex', 'iface_index')
6199
6200 start_networkd()
6201 self.wait_online('veth-peer:carrier')
6202 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6203 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
6204 '--dhcp-option=option:domain-search,example.com',
6205 '--dhcp-alternate-port=67,5555',
6206 ipv4_range='192.168.5.110,192.168.5.119')
6207 self.wait_online('veth99:routable', 'veth-peer:routable')
6208 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6209
6210 print('## ip address show dev veth99 scope global')
6211 output = check_output('ip address show dev veth99 scope global')
6212 print(output)
6213 self.assertIn('mtu 1492', output)
6214 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6215 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')
6216 self.assertNotIn('2600::', output)
6217
6218 output = check_output('ip -4 --json address show dev veth99')
6219 for i in json.loads(output)[0]['addr_info']:
6220 if i['label'] == 'test-label':
6221 address1 = i['local']
6222 break
6223 else:
6224 self.assertFalse(True)
6225
6226 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
6227
6228 print('## ip route show table main dev veth99')
6229 output = check_output('ip route show table main dev veth99')
6230 print(output)
6231 # no DHCP routes assigned to the main table
6232 self.assertNotIn('proto dhcp', output)
6233 # static routes
6234 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6235 self.assertIn('192.168.5.0/24 proto static scope link', output)
6236 self.assertIn('192.168.6.0/24 proto static scope link', output)
6237 self.assertIn('192.168.7.0/24 proto static scope link', output)
6238
6239 print('## ip route show table 211 dev veth99')
6240 output = check_output('ip route show table 211 dev veth99')
6241 print(output)
6242 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6243 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6244 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6245 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6246 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6247 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6248
6249 print('## link state file')
6250 output = read_link_state_file('veth99')
6251 print(output)
6252 # checking DNS server, SIP server, and Domains
6253 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
6254 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
6255 self.assertIn('DOMAINS=example.com', output)
6256
6257 print('## json')
6258 j = json.loads(networkctl_json('veth99'))
6259
6260 self.assertEqual(len(j['DNS']), 2)
6261 for i in j['DNS']:
6262 print(i)
6263 self.assertEqual(i['Family'], 2)
6264 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6265 self.assertRegex(a, '^192.168.5.[67]$')
6266 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6267 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6268 self.assertEqual('192.168.5.1', a)
6269
6270 self.assertEqual(len(j['SIP']), 2)
6271 for i in j['SIP']:
6272 print(i)
6273 self.assertEqual(i['Family'], 2)
6274 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6275 self.assertRegex(a, '^192.168.5.2[12]$')
6276 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6277 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6278 self.assertEqual('192.168.5.1', a)
6279
6280 print('## dnsmasq log')
6281 output = read_dnsmasq_log_file()
6282 print(output)
6283 self.assertIn('vendor class: FooBarVendorTest', output)
6284 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
6285 self.assertIn('client provides name: test-hostname', output)
6286 self.assertIn('26:mtu', output)
6287
6288 # change address range, DNS servers, and Domains
6289 stop_dnsmasq()
6290 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6291 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6292 '--dhcp-option=option:domain-search,foo.example.com',
6293 '--dhcp-alternate-port=67,5555',
6294 ipv4_range='192.168.5.120,192.168.5.129',)
6295
6296 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6297 print('Wait for the DHCP lease to be expired')
6298 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
6299 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
6300
6301 self.wait_online('veth99:routable', 'veth-peer:routable')
6302
6303 print('## ip address show dev veth99 scope global')
6304 output = check_output('ip address show dev veth99 scope global')
6305 print(output)
6306 self.assertIn('mtu 1492', output)
6307 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6308 self.assertNotIn(f'{address1}', output)
6309 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')
6310 self.assertNotIn('2600::', output)
6311
6312 output = check_output('ip -4 --json address show dev veth99')
6313 for i in json.loads(output)[0]['addr_info']:
6314 if i['label'] == 'test-label':
6315 address2 = i['local']
6316 break
6317 else:
6318 self.assertFalse(True)
6319
6320 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
6321
6322 print('## ip route show table main dev veth99')
6323 output = check_output('ip route show table main dev veth99')
6324 print(output)
6325 # no DHCP routes assigned to the main table
6326 self.assertNotIn('proto dhcp', output)
6327 # static routes
6328 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6329 self.assertIn('192.168.5.0/24 proto static scope link', output)
6330 self.assertIn('192.168.6.0/24 proto static scope link', output)
6331 self.assertIn('192.168.7.0/24 proto static scope link', output)
6332
6333 print('## ip route show table 211 dev veth99')
6334 output = check_output('ip route show table 211 dev veth99')
6335 print(output)
6336 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6337 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6338 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6339 self.assertNotIn('192.168.5.6', output)
6340 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6341 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6342 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6343
6344 print('## link state file')
6345 output = read_link_state_file('veth99')
6346 print(output)
6347 # checking DNS server, SIP server, and Domains
6348 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
6349 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
6350 self.assertIn('DOMAINS=foo.example.com', output)
6351
6352 print('## json')
6353 j = json.loads(networkctl_json('veth99'))
6354
6355 self.assertEqual(len(j['DNS']), 3)
6356 for i in j['DNS']:
6357 print(i)
6358 self.assertEqual(i['Family'], 2)
6359 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6360 self.assertRegex(a, '^192.168.5.[178]$')
6361 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6362 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6363 self.assertEqual('192.168.5.1', a)
6364
6365 self.assertEqual(len(j['SIP']), 2)
6366 for i in j['SIP']:
6367 print(i)
6368 self.assertEqual(i['Family'], 2)
6369 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6370 self.assertRegex(a, '^192.168.5.2[34]$')
6371 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6372 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6373 self.assertEqual('192.168.5.1', a)
6374
6375 print('## dnsmasq log')
6376 output = read_dnsmasq_log_file()
6377 print(output)
6378 self.assertIn('vendor class: FooBarVendorTest', output)
6379 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
6380 self.assertIn('client provides name: test-hostname', output)
6381 self.assertIn('26:mtu', output)
6382
6383 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
6384
6385 self.check_nftset('addr4', r'192\.168\.5\.1')
6386 self.check_nftset('network4', r'192\.168\.5\.0/24')
6387 self.check_nftset('ifindex', 'veth99')
6388
6389 self.teardown_nftset('addr4', 'network4', 'ifindex')
6390
6391 def test_dhcp_client_ipv4_dbus_status(self):
6392 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6393 start_networkd()
6394 self.wait_online('veth-peer:carrier')
6395
6396 state = get_dhcp4_client_state('veth99')
6397 print(f"State = {state}")
6398 self.assertEqual(state, 'rebooting')
6399
6400 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6401 '--dhcp-option=option:domain-search,example.com',
6402 '--dhcp-alternate-port=67,5555',
6403 ipv4_range='192.168.5.110,192.168.5.119')
6404 self.wait_online('veth99:routable', 'veth-peer:routable')
6405 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6406
6407 state = get_dhcp4_client_state('veth99')
6408 print(f"State = {state}")
6409 self.assertEqual(state, 'bound')
6410
6411 def test_dhcp_client_allow_list(self):
6412 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
6413
6414 start_networkd()
6415 self.wait_online('veth-peer:carrier')
6416 since = datetime.datetime.now()
6417 start_dnsmasq()
6418
6419 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6420 for _ in range(20):
6421 if expect in read_networkd_log(since=since):
6422 break
6423 time.sleep(0.5)
6424 else:
6425 self.fail()
6426
6427 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6428 since = datetime.datetime.now()
6429 networkctl_reload()
6430
6431 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6432 for _ in range(20):
6433 if expect in read_networkd_log(since=since):
6434 break
6435 time.sleep(0.5)
6436 else:
6437 self.fail()
6438
6439 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6440 since = datetime.datetime.now()
6441 networkctl_reload()
6442
6443 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6444 for _ in range(20):
6445 if expect in read_networkd_log(since=since):
6446 break
6447 time.sleep(0.5)
6448 else:
6449 self.fail()
6450
6451 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
6452 def test_dhcp_client_rapid_commit(self):
6453 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6454 start_networkd()
6455 self.wait_online('veth-peer:carrier')
6456
6457 start_dnsmasq('--dhcp-rapid-commit')
6458 self.wait_online('veth99:routable', 'veth-peer:routable')
6459 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6460
6461 state = get_dhcp4_client_state('veth99')
6462 print(f"DHCPv4 client state = {state}")
6463 self.assertEqual(state, 'bound')
6464
6465 output = read_dnsmasq_log_file()
6466 self.assertIn('DHCPDISCOVER(veth-peer)', output)
6467 self.assertNotIn('DHCPOFFER(veth-peer)', output)
6468 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6469 self.assertIn('DHCPACK(veth-peer)', output)
6470
6471 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
6472 copy_network_unit('25-veth.netdev',
6473 '25-dhcp-server-ipv6-only-mode.network',
6474 '25-dhcp-client-ipv6-only-mode.network')
6475 start_networkd()
6476 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
6477 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6478
6479 state = get_dhcp4_client_state('veth99')
6480 print(f"State = {state}")
6481 self.assertEqual(state, 'bound')
6482
6483 def test_dhcp_client_ipv4_use_routes_gateway(self):
6484 first = True
6485 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
6486 if first:
6487 first = False
6488 else:
6489 self.tearDown()
6490
6491 print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6492 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
6493 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
6494
6495 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
6496 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6497 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
6498 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
6499 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
6500 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6501 copy_network_unit(*testunits, copy_dropins=False)
6502
6503 start_networkd()
6504 self.wait_online('veth-peer:carrier')
6505 additional_options = [
6506 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6507 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6508 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6509 ]
6510 if classless:
6511 additional_options += [
6512 '--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'
6513 ]
6514 start_dnsmasq(*additional_options)
6515 self.wait_online('veth99:routable', 'veth-peer:routable')
6516
6517 output = check_output('ip -4 route show dev veth99')
6518 print(output)
6519
6520 # Check UseRoutes=
6521 if use_routes:
6522 if classless:
6523 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6524 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6525 self.assertRegex(output, r'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6526 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6527 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6528 else:
6529 self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6530 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6531 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6532 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6533 else:
6534 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6535 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6536 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6537 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6538 self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6539 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6540 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6541 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6542
6543 # Check UseGateway=
6544 if use_gateway and (not classless or not use_routes):
6545 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6546 else:
6547 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6548
6549 # Check route to gateway
6550 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
6551 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6552 else:
6553 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6554
6555 # Check RoutesToDNS= and RoutesToNTP=
6556 if dns_and_ntp_routes:
6557 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6558 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6559 if use_routes:
6560 if classless:
6561 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6562 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6563 else:
6564 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6565 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6566 else:
6567 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6568 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6569 else:
6570 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6571 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6572 self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6573 self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6574
6575 check_json(networkctl_json())
6576
6577 def test_dhcp_client_settings_anonymize(self):
6578 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6579 start_networkd()
6580 self.wait_online('veth-peer:carrier')
6581 start_dnsmasq()
6582 self.wait_online('veth99:routable', 'veth-peer:routable')
6583
6584 print('## dnsmasq log')
6585 output = read_dnsmasq_log_file()
6586 print(output)
6587 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
6588 self.assertNotIn('test-hostname', output)
6589 self.assertNotIn('26:mtu', output)
6590
6591 def test_dhcp_keep_configuration_dhcp(self):
6592 copy_network_unit('25-veth.netdev',
6593 '25-dhcp-server-veth-peer.network',
6594 '25-dhcp-client-keep-configuration-dhcp.network')
6595 start_networkd()
6596 self.wait_online('veth-peer:carrier')
6597 start_dnsmasq()
6598 self.wait_online('veth99:routable', 'veth-peer:routable')
6599
6600 output = check_output('ip address show dev veth99 scope global')
6601 print(output)
6602 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6603 'valid_lft forever preferred_lft forever')
6604
6605 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6606 stop_dnsmasq()
6607
6608 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6609 print('Wait for the DHCP lease to be expired')
6610 time.sleep(120)
6611
6612 # The lease address should be kept after the lease expired
6613 output = check_output('ip address show dev veth99 scope global')
6614 print(output)
6615 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6616 'valid_lft forever preferred_lft forever')
6617
6618 stop_networkd()
6619
6620 # The lease address should be kept after networkd stopped
6621 output = check_output('ip address show dev veth99 scope global')
6622 print(output)
6623 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6624 'valid_lft forever preferred_lft forever')
6625
6626 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
6627 f.write('[Network]\nDHCP=no\n')
6628
6629 start_networkd()
6630 self.wait_online('veth99:routable', 'veth-peer:routable')
6631
6632 # Still the lease address should be kept after networkd restarted
6633 output = check_output('ip address show dev veth99 scope global')
6634 print(output)
6635 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6636 'valid_lft forever preferred_lft forever')
6637
6638 def test_dhcp_keep_configuration_dhcp_on_stop(self):
6639 copy_network_unit('25-veth.netdev',
6640 '25-dhcp-server-veth-peer.network',
6641 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6642 start_networkd()
6643 self.wait_online('veth-peer:carrier')
6644 start_dnsmasq()
6645 self.wait_online('veth99:routable', 'veth-peer:routable')
6646
6647 output = check_output('ip address show dev veth99 scope global')
6648 print(output)
6649 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6650
6651 stop_dnsmasq()
6652 stop_networkd()
6653
6654 output = check_output('ip address show dev veth99 scope global')
6655 print(output)
6656 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6657
6658 start_networkd()
6659 self.wait_online('veth-peer:routable')
6660
6661 output = check_output('ip address show dev veth99 scope global')
6662 print(output)
6663 self.assertNotIn('192.168.5.', output)
6664
6665 def test_dhcp_client_reuse_address_as_static(self):
6666 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6667 start_networkd()
6668 self.wait_online('veth-peer:carrier')
6669 start_dnsmasq()
6670 self.wait_online('veth99:routable', 'veth-peer:routable')
6671
6672 # link become 'routable' when at least one protocol provide an valid address.
6673 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6674 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6675
6676 output = check_output('ip address show dev veth99 scope global')
6677 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
6678 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
6679 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
6680 print(static_network)
6681
6682 remove_network_unit('25-dhcp-client.network')
6683
6684 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
6685 f.write(static_network)
6686
6687 restart_networkd()
6688 self.wait_online('veth99:routable')
6689
6690 output = check_output('ip -4 address show dev veth99 scope global')
6691 print(output)
6692 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6693 'valid_lft forever preferred_lft forever')
6694
6695 output = check_output('ip -6 address show dev veth99 scope global')
6696 print(output)
6697 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
6698 'valid_lft forever preferred_lft forever')
6699
6700 @expectedFailureIfModuleIsNotAvailable('vrf')
6701 def test_dhcp_client_vrf(self):
6702 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6703 '25-vrf.netdev', '25-vrf.network')
6704 start_networkd()
6705 self.wait_online('veth-peer:carrier')
6706 start_dnsmasq()
6707 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6708
6709 # link become 'routable' when at least one protocol provide an valid address.
6710 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6711 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6712
6713 print('## ip -d link show dev vrf99')
6714 output = check_output('ip -d link show dev vrf99')
6715 print(output)
6716 self.assertRegex(output, 'vrf table 42')
6717
6718 print('## ip address show vrf vrf99')
6719 output = check_output('ip address show vrf vrf99')
6720 print(output)
6721 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6722 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6723 self.assertRegex(output, 'inet6 .* scope link')
6724
6725 print('## ip address show dev veth99')
6726 output = check_output('ip address show dev veth99')
6727 print(output)
6728 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6729 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6730 self.assertRegex(output, 'inet6 .* scope link')
6731
6732 print('## ip route show vrf vrf99')
6733 output = check_output('ip route show vrf vrf99')
6734 print(output)
6735 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6736 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6737 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6738
6739 print('## ip route show table main dev veth99')
6740 output = check_output('ip route show table main dev veth99')
6741 print(output)
6742 self.assertEqual(output, '')
6743
6744 def test_dhcp_client_gateway_onlink_implicit(self):
6745 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6746 '25-dhcp-client-gateway-onlink-implicit.network')
6747 start_networkd()
6748 self.wait_online('veth-peer:carrier')
6749 start_dnsmasq()
6750 self.wait_online('veth99:routable', 'veth-peer:routable')
6751
6752 output = networkctl_status('veth99')
6753 print(output)
6754 self.assertRegex(output, '192.168.5')
6755
6756 output = check_output('ip route list dev veth99 10.0.0.0/8')
6757 print(output)
6758 self.assertRegex(output, 'onlink')
6759 output = check_output('ip route list dev veth99 192.168.100.0/24')
6760 print(output)
6761 self.assertRegex(output, 'onlink')
6762
6763 def test_dhcp_client_with_ipv4ll(self):
6764 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6765 '25-dhcp-client-with-ipv4ll.network')
6766 start_networkd()
6767 # we need to increase timeout above default, as this will need to wait for
6768 # systemd-networkd to get the dhcpv4 transient failure event
6769 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
6770
6771 output = check_output('ip -4 address show dev veth99')
6772 print(output)
6773 self.assertNotIn('192.168.5.', output)
6774 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
6775
6776 start_dnsmasq()
6777 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6778 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
6779 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')
6780 self.wait_online('veth99:routable')
6781
6782 output = check_output('ip -4 address show dev veth99')
6783 print(output)
6784 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6785 self.assertNotIn('169.254.', output)
6786 self.assertNotIn('scope link', output)
6787
6788 stop_dnsmasq()
6789 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6790 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)
6791 self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
6792
6793 output = check_output('ip -4 address show dev veth99')
6794 print(output)
6795 self.assertNotIn('192.168.5.', output)
6796 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
6797
6798 def test_dhcp_client_use_dns(self):
6799 def check(self, ipv4, ipv6):
6800 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6801 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6802 f.write('[DHCPv4]\nUseDNS=')
6803 f.write('yes' if ipv4 else 'no')
6804 f.write('\n[DHCPv6]\nUseDNS=')
6805 f.write('yes' if ipv6 else 'no')
6806 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
6807
6808 networkctl_reload()
6809 self.wait_online('veth99:routable')
6810
6811 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6812 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6813 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6814
6815 # make resolved re-read the link state file
6816 resolvectl('revert', 'veth99')
6817
6818 output = resolvectl('dns', 'veth99')
6819 print(output)
6820 if ipv4:
6821 self.assertIn('192.168.5.1', output)
6822 else:
6823 self.assertNotIn('192.168.5.1', output)
6824 if ipv6:
6825 self.assertIn('2600::1', output)
6826 else:
6827 self.assertNotIn('2600::1', output)
6828
6829 check_json(networkctl_json())
6830
6831 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6832
6833 start_networkd()
6834 self.wait_online('veth-peer:carrier')
6835 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6836 '--dhcp-option=option6:dns-server,[2600::1]')
6837
6838 check(self, True, True)
6839 check(self, True, False)
6840 check(self, False, True)
6841 check(self, False, False)
6842
6843 def test_dhcp_client_use_captive_portal(self):
6844 def check(self, ipv4, ipv6):
6845 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6846 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6847 f.write('[DHCPv4]\nUseCaptivePortal=')
6848 f.write('yes' if ipv4 else 'no')
6849 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6850 f.write('yes' if ipv6 else 'no')
6851 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6852
6853 networkctl_reload()
6854 self.wait_online('veth99:routable')
6855
6856 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6857 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6858 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6859
6860 output = networkctl_status('veth99')
6861 print(output)
6862 if ipv4 or ipv6:
6863 self.assertIn('Captive Portal: http://systemd.io', output)
6864 else:
6865 self.assertNotIn('Captive Portal: http://systemd.io', output)
6866
6867 check_json(networkctl_json())
6868
6869 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6870
6871 start_networkd()
6872 self.wait_online('veth-peer:carrier')
6873 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6874 '--dhcp-option=option6:103,http://systemd.io')
6875
6876 check(self, True, True)
6877 check(self, True, False)
6878 check(self, False, True)
6879 check(self, False, False)
6880
6881 def test_dhcp_client_reject_captive_portal(self):
6882 def check(self, ipv4, ipv6):
6883 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6884 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6885 f.write('[DHCPv4]\nUseCaptivePortal=')
6886 f.write('yes' if ipv4 else 'no')
6887 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6888 f.write('yes' if ipv6 else 'no')
6889 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6890
6891 networkctl_reload()
6892 self.wait_online('veth99:routable')
6893
6894 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6895 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6896 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6897
6898 output = networkctl_status('veth99')
6899 print(output)
6900 self.assertNotIn('Captive Portal: ', output)
6901 self.assertNotIn('invalid/url', output)
6902
6903 check_json(networkctl_json())
6904
6905 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6906
6907 start_networkd()
6908 self.wait_online('veth-peer:carrier')
6909 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
6910 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
6911 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
6912
6913 check(self, True, True)
6914 check(self, True, False)
6915 check(self, False, True)
6916 check(self, False, False)
6917
6918 class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
6919
6920 def setUp(self):
6921 setup_common()
6922
6923 def tearDown(self):
6924 tear_down_common()
6925
6926 def check_dhcp6_prefix(self, link):
6927 description = get_link_description(link)
6928
6929 self.assertIn('DHCPv6Client', description.keys())
6930 self.assertIn('Prefixes', description['DHCPv6Client'])
6931
6932 prefixInfo = description['DHCPv6Client']['Prefixes']
6933
6934 self.assertEqual(len(prefixInfo), 1)
6935
6936 self.assertIn('Prefix', prefixInfo[0].keys())
6937 self.assertIn('PrefixLength', prefixInfo[0].keys())
6938 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
6939 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
6940
6941 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6942 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
6943 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
6944 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
6945
6946 def test_dhcp6pd_no_address(self):
6947 # For issue #29979.
6948 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6949
6950 start_networkd()
6951 self.wait_online('veth-peer:routable')
6952 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
6953 self.wait_online('veth99:degraded')
6954
6955 print('### ip -6 address show dev veth99 scope global')
6956 output = check_output('ip -6 address show dev veth99 scope global')
6957 print(output)
6958 self.assertNotIn('inet6 3ffe:501:ffff', output)
6959
6960 self.check_dhcp6_prefix('veth99')
6961
6962 def test_dhcp6pd_no_assign(self):
6963 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6964 # However, the server does not provide IA_NA. For issue #31349.
6965 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6966
6967 start_networkd()
6968 self.wait_online('veth-peer:routable')
6969 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
6970 self.wait_online('veth99:degraded')
6971
6972 print('### ip -6 address show dev veth99 scope global')
6973 output = check_output('ip -6 address show dev veth99 scope global')
6974 print(output)
6975 self.assertNotIn('inet6 3ffe:501:ffff', output)
6976
6977 self.check_dhcp6_prefix('veth99')
6978
6979 def test_dhcp6pd(self):
6980 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6981 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6982 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6983 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6984 '25-dhcp-pd-downstream-dummy97.network',
6985 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6986 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6987
6988 start_networkd()
6989 self.wait_online('veth-peer:routable')
6990 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
6991 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6992 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6993
6994 self.setup_nftset('addr6', 'ipv6_addr')
6995 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6996 self.setup_nftset('ifindex', 'iface_index')
6997
6998 # Check DBus assigned prefix information to veth99
6999 self.check_dhcp6_prefix('veth99')
7000
7001 print('### ip -6 address show dev veth-peer scope global')
7002 output = check_output('ip -6 address show dev veth-peer scope global')
7003 print(output)
7004 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
7005
7006 # Link Subnet IDs
7007 # test1: 0x00
7008 # dummy97: 0x01 (The link will appear later)
7009 # dummy98: 0x00
7010 # dummy99: auto -> 0x02 (No address assignment)
7011 # veth97: 0x08
7012 # veth98: 0x09
7013 # veth99: 0x10
7014
7015 print('### ip -6 address show dev veth99 scope global')
7016 output = check_output('ip -6 address show dev veth99 scope global')
7017 print(output)
7018 # IA_NA
7019 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
7020 # address in IA_PD (Token=static)
7021 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
7022 # address in IA_PD (Token=eui64)
7023 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
7024 # address in IA_PD (temporary)
7025 # Note that the temporary addresses may appear after the link enters configured state
7026 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')
7027
7028 print('### ip -6 address show dev test1 scope global')
7029 output = check_output('ip -6 address show dev test1 scope global')
7030 print(output)
7031 # address in IA_PD (Token=static)
7032 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7033 # address in IA_PD (temporary)
7034 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')
7035
7036 print('### ip -6 address show dev dummy98 scope global')
7037 output = check_output('ip -6 address show dev dummy98 scope global')
7038 print(output)
7039 # address in IA_PD (Token=static)
7040 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7041 # address in IA_PD (temporary)
7042 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')
7043
7044 print('### ip -6 address show dev dummy99 scope global')
7045 output = check_output('ip -6 address show dev dummy99 scope global')
7046 print(output)
7047 # Assign=no
7048 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
7049
7050 print('### ip -6 address show dev veth97 scope global')
7051 output = check_output('ip -6 address show dev veth97 scope global')
7052 print(output)
7053 # address in IA_PD (Token=static)
7054 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7055 # address in IA_PD (Token=eui64)
7056 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7057 # address in IA_PD (temporary)
7058 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')
7059
7060 print('### ip -6 address show dev veth97-peer scope global')
7061 output = check_output('ip -6 address show dev veth97-peer scope global')
7062 print(output)
7063 # NDisc address (Token=static)
7064 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7065 # NDisc address (Token=eui64)
7066 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7067 # NDisc address (temporary)
7068 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')
7069
7070 print('### ip -6 address show dev veth98 scope global')
7071 output = check_output('ip -6 address show dev veth98 scope global')
7072 print(output)
7073 # address in IA_PD (Token=static)
7074 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7075 # address in IA_PD (Token=eui64)
7076 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7077 # address in IA_PD (temporary)
7078 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')
7079
7080 print('### ip -6 address show dev veth98-peer scope global')
7081 output = check_output('ip -6 address show dev veth98-peer scope global')
7082 print(output)
7083 # NDisc address (Token=static)
7084 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7085 # NDisc address (Token=eui64)
7086 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7087 # NDisc address (temporary)
7088 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')
7089
7090 print('### ip -6 route show type unreachable')
7091 output = check_output('ip -6 route show type unreachable')
7092 print(output)
7093 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
7094
7095 print('### ip -6 route show dev veth99')
7096 output = check_output('ip -6 route show dev veth99')
7097 print(output)
7098 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
7099
7100 print('### ip -6 route show dev test1')
7101 output = check_output('ip -6 route show dev test1')
7102 print(output)
7103 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7104
7105 print('### ip -6 route show dev dummy98')
7106 output = check_output('ip -6 route show dev dummy98')
7107 print(output)
7108 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7109
7110 print('### ip -6 route show dev dummy99')
7111 output = check_output('ip -6 route show dev dummy99')
7112 print(output)
7113 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
7114
7115 print('### ip -6 route show dev veth97')
7116 output = check_output('ip -6 route show dev veth97')
7117 print(output)
7118 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
7119
7120 print('### ip -6 route show dev veth97-peer')
7121 output = check_output('ip -6 route show dev veth97-peer')
7122 print(output)
7123 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
7124
7125 print('### ip -6 route show dev veth98')
7126 output = check_output('ip -6 route show dev veth98')
7127 print(output)
7128 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
7129
7130 print('### ip -6 route show dev veth98-peer')
7131 output = check_output('ip -6 route show dev veth98-peer')
7132 print(output)
7133 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
7134
7135 # Test case for a downstream which appears later
7136 check_output('ip link add dummy97 type dummy')
7137 self.wait_online('dummy97:routable')
7138
7139 print('### ip -6 address show dev dummy97 scope global')
7140 output = check_output('ip -6 address show dev dummy97 scope global')
7141 print(output)
7142 # address in IA_PD (Token=static)
7143 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7144 # address in IA_PD (temporary)
7145 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')
7146
7147 print('### ip -6 route show dev dummy97')
7148 output = check_output('ip -6 route show dev dummy97')
7149 print(output)
7150 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
7151
7152 # Test case for reconfigure
7153 networkctl_reconfigure('dummy98', 'dummy99')
7154 self.wait_online('dummy98:routable', 'dummy99:degraded')
7155
7156 print('### ip -6 address show dev dummy98 scope global')
7157 output = check_output('ip -6 address show dev dummy98 scope global')
7158 print(output)
7159 # address in IA_PD (Token=static)
7160 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7161 # address in IA_PD (temporary)
7162 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')
7163
7164 print('### ip -6 address show dev dummy99 scope global')
7165 output = check_output('ip -6 address show dev dummy99 scope global')
7166 print(output)
7167 # Assign=no
7168 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
7169
7170 print('### ip -6 route show dev dummy98')
7171 output = check_output('ip -6 route show dev dummy98')
7172 print(output)
7173 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7174
7175 print('### ip -6 route show dev dummy99')
7176 output = check_output('ip -6 route show dev dummy99')
7177 print(output)
7178 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
7179
7180 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
7181
7182 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
7183 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
7184 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
7185 self.check_nftset('ifindex', 'dummy98')
7186
7187 self.teardown_nftset('addr6', 'network6', 'ifindex')
7188
7189 def verify_dhcp4_6rd(self, tunnel_name):
7190 print('### ip -4 address show dev veth-peer scope global')
7191 output = check_output('ip -4 address show dev veth-peer scope global')
7192 print(output)
7193 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
7194
7195 # Link Subnet IDs
7196 # test1: 0x00
7197 # dummy97: 0x01 (The link will appear later)
7198 # dummy98: 0x00
7199 # dummy99: auto -> 0x0[23] (No address assignment)
7200 # 6rd-XXX: auto -> 0x0[23]
7201 # veth97: 0x08
7202 # veth98: 0x09
7203 # veth99: 0x10
7204
7205 print('### ip -4 address show dev veth99 scope global')
7206 output = check_output('ip -4 address show dev veth99 scope global')
7207 print(output)
7208 self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7209
7210 print('### ip -6 address show dev veth99 scope global')
7211 output = check_output('ip -6 address show dev veth99 scope global')
7212 print(output)
7213 # address in IA_PD (Token=static)
7214 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7215 # address in IA_PD (Token=eui64)
7216 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7217 # address in IA_PD (temporary)
7218 # Note that the temporary addresses may appear after the link enters configured state
7219 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')
7220
7221 print('### ip -6 address show dev test1 scope global')
7222 output = check_output('ip -6 address show dev test1 scope global')
7223 print(output)
7224 # address in IA_PD (Token=static)
7225 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7226 # address in IA_PD (temporary)
7227 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')
7228
7229 print('### ip -6 address show dev dummy98 scope global')
7230 output = check_output('ip -6 address show dev dummy98 scope global')
7231 print(output)
7232 # address in IA_PD (Token=static)
7233 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7234 # address in IA_PD (temporary)
7235 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')
7236
7237 print('### ip -6 address show dev dummy99 scope global')
7238 output = check_output('ip -6 address show dev dummy99 scope global')
7239 print(output)
7240 # Assign=no
7241 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7242
7243 print('### ip -6 address show dev veth97 scope global')
7244 output = check_output('ip -6 address show dev veth97 scope global')
7245 print(output)
7246 # address in IA_PD (Token=static)
7247 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7248 # address in IA_PD (Token=eui64)
7249 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7250 # address in IA_PD (temporary)
7251 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')
7252
7253 print('### ip -6 address show dev veth97-peer scope global')
7254 output = check_output('ip -6 address show dev veth97-peer scope global')
7255 print(output)
7256 # NDisc address (Token=static)
7257 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7258 # NDisc address (Token=eui64)
7259 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7260 # NDisc address (temporary)
7261 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')
7262
7263 print('### ip -6 address show dev veth98 scope global')
7264 output = check_output('ip -6 address show dev veth98 scope global')
7265 print(output)
7266 # address in IA_PD (Token=static)
7267 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7268 # address in IA_PD (Token=eui64)
7269 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7270 # address in IA_PD (temporary)
7271 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')
7272
7273 print('### ip -6 address show dev veth98-peer scope global')
7274 output = check_output('ip -6 address show dev veth98-peer scope global')
7275 print(output)
7276 # NDisc address (Token=static)
7277 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7278 # NDisc address (Token=eui64)
7279 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7280 # NDisc address (temporary)
7281 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')
7282
7283 print('### ip -6 route show type unreachable')
7284 output = check_output('ip -6 route show type unreachable')
7285 print(output)
7286 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7287
7288 print('### ip -6 route show dev veth99')
7289 output = check_output('ip -6 route show dev veth99')
7290 print(output)
7291 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7292
7293 print('### ip -6 route show dev test1')
7294 output = check_output('ip -6 route show dev test1')
7295 print(output)
7296 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7297
7298 print('### ip -6 route show dev dummy98')
7299 output = check_output('ip -6 route show dev dummy98')
7300 print(output)
7301 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7302
7303 print('### ip -6 route show dev dummy99')
7304 output = check_output('ip -6 route show dev dummy99')
7305 print(output)
7306 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7307
7308 print('### ip -6 route show dev veth97')
7309 output = check_output('ip -6 route show dev veth97')
7310 print(output)
7311 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7312
7313 print('### ip -6 route show dev veth97-peer')
7314 output = check_output('ip -6 route show dev veth97-peer')
7315 print(output)
7316 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7317
7318 print('### ip -6 route show dev veth98')
7319 output = check_output('ip -6 route show dev veth98')
7320 print(output)
7321 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7322
7323 print('### ip -6 route show dev veth98-peer')
7324 output = check_output('ip -6 route show dev veth98-peer')
7325 print(output)
7326 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7327
7328 print('### ip -6 address show dev dummy97 scope global')
7329 output = check_output('ip -6 address show dev dummy97 scope global')
7330 print(output)
7331 # address in IA_PD (Token=static)
7332 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7333 # address in IA_PD (temporary)
7334 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')
7335
7336 print('### ip -6 route show dev dummy97')
7337 output = check_output('ip -6 route show dev dummy97')
7338 print(output)
7339 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7340
7341 print(f'### ip -d link show dev {tunnel_name}')
7342 output = check_output(f'ip -d link show dev {tunnel_name}')
7343 print(output)
7344 self.assertIn('link/sit 10.100.100.', output)
7345 self.assertIn('local 10.100.100.', output)
7346 self.assertIn('ttl 64', output)
7347 self.assertIn('6rd-prefix 2001:db8::/32', output)
7348 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
7349
7350 print(f'### ip -6 address show dev {tunnel_name}')
7351 output = check_output(f'ip -6 address show dev {tunnel_name}')
7352 print(output)
7353 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')
7354 self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7355
7356 print(f'### ip -6 route show dev {tunnel_name}')
7357 output = check_output(f'ip -6 route show dev {tunnel_name}')
7358 print(output)
7359 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7360 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
7361
7362 print('### ip -6 route show default')
7363 output = check_output('ip -6 route show default')
7364 print(output)
7365 self.assertIn('default', output)
7366 self.assertIn(f'via ::10.0.0.1 dev {tunnel_name}', output)
7367
7368 def test_dhcp4_6rd(self):
7369 def get_dhcp_6rd_prefix(link):
7370 description = get_link_description(link)
7371
7372 self.assertIn('DHCPv4Client', description.keys())
7373 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
7374
7375 prefixInfo = description['DHCPv4Client']['6rdPrefix']
7376 self.assertIn('Prefix', prefixInfo.keys())
7377 self.assertIn('PrefixLength', prefixInfo.keys())
7378 self.assertIn('IPv4MaskLength', prefixInfo.keys())
7379 self.assertIn('BorderRouters', prefixInfo.keys())
7380
7381 return prefixInfo
7382
7383 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7384 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7385 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7386 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7387 '25-dhcp-pd-downstream-dummy97.network',
7388 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7389 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7390 '80-6rd-tunnel.network')
7391
7392 start_networkd()
7393 self.wait_online('veth-peer:routable')
7394
7395 # ipv4masklen: 8
7396 # 6rd-prefix: 2001:db8::/32
7397 # br-addresss: 10.0.0.1
7398
7399 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',
7400 ipv4_range='10.100.100.100,10.100.100.200',
7401 ipv4_router='10.0.0.1')
7402 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7403 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7404
7405 # Check the DBus interface for assigned prefix information
7406 prefixInfo = get_dhcp_6rd_prefix('veth99')
7407
7408 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7409 self.assertEqual(prefixInfo['PrefixLength'], 32)
7410 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
7411 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
7412
7413 # Test case for a downstream which appears later
7414 check_output('ip link add dummy97 type dummy')
7415 self.wait_online('dummy97:routable')
7416
7417 # Find tunnel name
7418 tunnel_name = None
7419 for name in os.listdir('/sys/class/net/'):
7420 if name.startswith('6rd-'):
7421 tunnel_name = name
7422 break
7423
7424 self.wait_online(f'{tunnel_name}:routable')
7425
7426 self.verify_dhcp4_6rd(tunnel_name)
7427
7428 # Test case for reconfigure
7429 networkctl_reconfigure('dummy98', 'dummy99')
7430 self.wait_online('dummy98:routable', 'dummy99:degraded')
7431
7432 self.verify_dhcp4_6rd(tunnel_name)
7433
7434 print('Wait for the DHCP lease to be renewed/rebind')
7435 time.sleep(120)
7436
7437 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7438 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7439
7440 self.verify_dhcp4_6rd(tunnel_name)
7441
7442 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
7443
7444 def setUp(self):
7445 setup_common()
7446
7447 def tearDown(self):
7448 tear_down_common()
7449
7450 def test_ipv6_route_prefix(self):
7451 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7452 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7453
7454 start_networkd()
7455 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7456
7457 output = check_output('ip address show dev veth-peer')
7458 print(output)
7459 self.assertIn('inet6 2001:db8:0:1:', output)
7460 self.assertNotIn('inet6 2001:db8:0:2:', output)
7461 self.assertNotIn('inet6 2001:db8:0:3:', output)
7462
7463 output = check_output('ip -6 route show dev veth-peer')
7464 print(output)
7465 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7466 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7467 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
7468 self.assertIn('2001:db0:fff::/64 via ', output)
7469 self.assertNotIn('2001:db1:fff::/64 via ', output)
7470 self.assertNotIn('2001:db2:fff::/64 via ', output)
7471
7472 output = check_output('ip address show dev veth99')
7473 print(output)
7474 self.assertNotIn('inet6 2001:db8:0:1:', output)
7475 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
7476 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
7477 self.assertNotIn('inet6 2001:db8:0:3:', output)
7478
7479 output = resolvectl('dns', 'veth-peer')
7480 print(output)
7481 self.assertRegex(output, '2001:db8:1:1::2')
7482
7483 output = resolvectl('domain', 'veth-peer')
7484 print(output)
7485 self.assertIn('example.com', output)
7486
7487 check_json(networkctl_json())
7488
7489 output = networkctl_json('veth-peer')
7490 check_json(output)
7491
7492 # PREF64 or NAT64
7493 pref64 = json.loads(output)['NDisc']['PREF64'][0]
7494
7495 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
7496 self.assertEqual(prefix, '64:ff9b::')
7497
7498 prefix_length = pref64['PrefixLength']
7499 self.assertEqual(prefix_length, 96)
7500
7501 def test_ipv6_route_prefix_deny_list(self):
7502 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7503 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7504
7505 start_networkd()
7506 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7507
7508 output = check_output('ip address show dev veth-peer')
7509 print(output)
7510 self.assertIn('inet6 2001:db8:0:1:', output)
7511 self.assertNotIn('inet6 2001:db8:0:2:', output)
7512
7513 output = check_output('ip -6 route show dev veth-peer')
7514 print(output)
7515 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7516 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7517 self.assertIn('2001:db0:fff::/64 via ', output)
7518 self.assertNotIn('2001:db1:fff::/64 via ', output)
7519
7520 output = check_output('ip address show dev veth99')
7521 print(output)
7522 self.assertNotIn('inet6 2001:db8:0:1:', output)
7523 self.assertIn('inet6 2001:db8:0:2:', output)
7524
7525 output = resolvectl('dns', 'veth-peer')
7526 print(output)
7527 self.assertRegex(output, '2001:db8:1:1::2')
7528
7529 output = resolvectl('domain', 'veth-peer')
7530 print(output)
7531 self.assertIn('example.com', output)
7532
7533 class NetworkdMTUTests(unittest.TestCase, Utilities):
7534
7535 def setUp(self):
7536 setup_common()
7537
7538 def tearDown(self):
7539 tear_down_common()
7540
7541 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
7542 if not ipv6_mtu:
7543 ipv6_mtu = mtu
7544
7545 # test normal start
7546 start_networkd()
7547 self.wait_online('dummy98:routable')
7548 self.check_link_attr('dummy98', 'mtu', mtu)
7549 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7550
7551 # test normal restart
7552 restart_networkd()
7553 self.wait_online('dummy98:routable')
7554 self.check_link_attr('dummy98', 'mtu', mtu)
7555 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7556
7557 if reset:
7558 self.reset_check_mtu(mtu, ipv6_mtu)
7559
7560 def reset_check_mtu(self, mtu, ipv6_mtu=None):
7561 ''' test setting mtu/ipv6_mtu with interface already up '''
7562 stop_networkd()
7563
7564 # note - changing the device mtu resets the ipv6 mtu
7565 check_output('ip link set up mtu 1501 dev dummy98')
7566 check_output('ip link set up mtu 1500 dev dummy98')
7567 self.check_link_attr('dummy98', 'mtu', '1500')
7568 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7569
7570 self.check_mtu(mtu, ipv6_mtu, reset=False)
7571
7572 def test_mtu_network(self):
7573 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7574 self.check_mtu('1600')
7575
7576 def test_mtu_netdev(self):
7577 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
7578 # note - MTU set by .netdev happens ONLY at device creation!
7579 self.check_mtu('1600', reset=False)
7580
7581 def test_mtu_link(self):
7582 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
7583 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7584 self.check_mtu('1600', reset=False)
7585
7586 def test_ipv6_mtu(self):
7587 ''' set ipv6 mtu without setting device mtu '''
7588 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7589 self.check_mtu('1500', '1400')
7590
7591 def test_ipv6_mtu_toolarge(self):
7592 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7593 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7594 self.check_mtu('1500', '1500')
7595
7596 def test_mtu_network_ipv6_mtu(self):
7597 ''' set ipv6 mtu and set device mtu via network file '''
7598 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7599 self.check_mtu('1600', '1550')
7600
7601 def test_mtu_netdev_ipv6_mtu(self):
7602 ''' set ipv6 mtu and set device mtu via netdev file '''
7603 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7604 self.check_mtu('1600', '1550', reset=False)
7605
7606 def test_mtu_link_ipv6_mtu(self):
7607 ''' set ipv6 mtu and set device mtu via link file '''
7608 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7609 self.check_mtu('1600', '1550', reset=False)
7610
7611
7612 if __name__ == '__main__':
7613 parser = argparse.ArgumentParser()
7614 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
7615 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
7616 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
7617 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
7618 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
7619 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
7620 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
7621 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)
7622 ns, unknown_args = parser.parse_known_args(namespace=unittest)
7623
7624 if ns.build_dir:
7625 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
7626 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
7627 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
7628 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
7629 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
7630 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
7631 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
7632 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
7633 build_dir = ns.build_dir
7634
7635 if ns.source_dir:
7636 source_dir = ns.source_dir
7637 else:
7638 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
7639 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
7640
7641 use_valgrind = ns.use_valgrind
7642 enable_debug = ns.enable_debug
7643 asan_options = ns.asan_options
7644 lsan_options = ns.lsan_options
7645 ubsan_options = ns.ubsan_options
7646 with_coverage = ns.with_coverage
7647
7648 if use_valgrind:
7649 # Do not forget the trailing space.
7650 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7651
7652 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
7653 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
7654 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
7655 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
7656 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
7657
7658 if build_dir:
7659 test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
7660 else:
7661 test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
7662
7663 if asan_options:
7664 env.update({'ASAN_OPTIONS': asan_options})
7665 if lsan_options:
7666 env.update({'LSAN_OPTIONS': lsan_options})
7667 if ubsan_options:
7668 env.update({'UBSAN_OPTIONS': ubsan_options})
7669 if use_valgrind:
7670 env.update({'SYSTEMD_MEMPOOL': '0'})
7671
7672 wait_online_env = env.copy()
7673 if enable_debug:
7674 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7675
7676 sys.argv[1:] = unknown_args
7677 unittest.main(verbosity=3)