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