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