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