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