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