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