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