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