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