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