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