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