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