]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
network: make [DHCPServer] in networkd.conf work
[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 # Check if the same addresses are used even if the service is restarted.
5775 restart_networkd()
5776 self.wait_online('veth99:routable', 'veth-peer:routable')
5777
5778 output = check_output('ip -4 address show dev veth-peer')
5779 print(output)
5780 self.assertIn(f'{server_address}', output)
5781
5782 output = check_output('ip -4 address show dev veth99')
5783 print(output)
5784 self.assertIn(f'{client_address}', output)
5785
5786 output = networkctl_status('veth99')
5787 print(output)
5788 self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
5789 self.assertIn(f'Gateway: {server_address}', output)
5790 self.assertIn(f'DNS: {server_address}', output)
5791 self.assertIn(f'NTP: {server_address}', output)
5792
5793 output = networkctl_status('veth-peer')
5794 self.assertIn(f'Offered DHCP leases: {client_address}', output)
5795
5796 def test_dhcp_server_with_uplink(self):
5797 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5798 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5799 start_networkd()
5800 self.wait_online('veth99:routable', 'veth-peer:routable')
5801
5802 output = networkctl_status('veth99')
5803 print(output)
5804 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5805 self.assertIn('Gateway: 192.168.5.3', output)
5806 self.assertIn('DNS: 192.168.5.1', output)
5807 self.assertIn('NTP: 192.168.5.1', output)
5808
5809 def test_emit_router_timezone(self):
5810 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5811 start_networkd()
5812 self.wait_online('veth99:routable', 'veth-peer:routable')
5813
5814 output = networkctl_status('veth99')
5815 print(output)
5816 self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5817 self.assertIn('Gateway: 192.168.5.1', output)
5818 self.assertIn('Time Zone: Europe/Berlin', output)
5819
5820 def test_dhcp_server_static_lease(self):
5821 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5822 start_networkd()
5823 self.wait_online('veth99:routable', 'veth-peer:routable')
5824
5825 output = networkctl_status('veth99')
5826 print(output)
5827 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
5828 self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output)
5829
5830 def test_dhcp_server_static_lease_default_client_id(self):
5831 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5832 start_networkd()
5833 self.wait_online('veth99:routable', 'veth-peer:routable')
5834
5835 output = networkctl_status('veth99')
5836 print(output)
5837 self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
5838 self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5839
5840 class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
5841
5842 def setUp(self):
5843 setup_common()
5844
5845 def tearDown(self):
5846 tear_down_common()
5847
5848 def test_relay_agent(self):
5849 copy_network_unit('25-agent-veth-client.netdev',
5850 '25-agent-veth-server.netdev',
5851 '25-agent-client.network',
5852 '25-agent-server.network',
5853 '25-agent-client-peer.network',
5854 '25-agent-server-peer.network')
5855 start_networkd()
5856
5857 self.wait_online('client:routable')
5858
5859 output = networkctl_status('client')
5860 print(output)
5861 self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5862
5863 def test_relay_agent_on_bridge(self):
5864 copy_network_unit('25-agent-bridge.netdev',
5865 '25-agent-veth-client.netdev',
5866 '25-agent-bridge.network',
5867 '25-agent-bridge-port.network',
5868 '25-agent-client.network')
5869 start_networkd()
5870 self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5871
5872 # For issue #30763.
5873 expect = 'bridge-relay: DHCPv4 server: STARTED'
5874 for _ in range(20):
5875 if expect in read_networkd_log():
5876 break
5877 time.sleep(0.5)
5878 else:
5879 self.fail()
5880
5881 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
5882
5883 def setUp(self):
5884 setup_common()
5885
5886 def tearDown(self):
5887 tear_down_common()
5888
5889 def test_dhcp_client_ipv6_only(self):
5890 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5891
5892 start_networkd()
5893 self.wait_online('veth-peer:carrier')
5894
5895 # information request mode
5896 # The name ipv6-only option may not be supported by older dnsmasq
5897 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5898 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5899 '--dhcp-option=option6:dns-server,[2600::ee]',
5900 '--dhcp-option=option6:ntp-server,[2600::ff]',
5901 ra_mode='ra-stateless')
5902 self.wait_online('veth99:routable', 'veth-peer:routable')
5903
5904 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5905 # Let's wait for the expected DNS server being listed in the state file.
5906 for _ in range(100):
5907 output = read_link_state_file('veth99')
5908 if 'DNS=2600::ee' in output:
5909 break
5910 time.sleep(.2)
5911
5912 # Check link state file
5913 print('## link state file')
5914 output = read_link_state_file('veth99')
5915 print(output)
5916 self.assertIn('DNS=2600::ee', output)
5917 self.assertIn('NTP=2600::ff', output)
5918
5919 # Check manager state file
5920 print('## manager state file')
5921 output = read_manager_state_file()
5922 print(output)
5923 self.assertRegex(output, 'DNS=.*2600::ee')
5924 self.assertRegex(output, 'NTP=.*2600::ff')
5925
5926 print('## dnsmasq log')
5927 output = read_dnsmasq_log_file()
5928 print(output)
5929 self.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
5930 self.assertNotIn('DHCPSOLICIT(veth-peer)', output)
5931 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
5932 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
5933 self.assertNotIn('DHCPREPLY(veth-peer)', output)
5934
5935 # Check json format
5936 check_json(networkctl_json('veth99'))
5937
5938 # solicit mode
5939 stop_dnsmasq()
5940 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5941 '--dhcp-option=option6:dns-server,[2600::ee]',
5942 '--dhcp-option=option6:ntp-server,[2600::ff]')
5943 networkctl_reconfigure('veth99')
5944 self.wait_online('veth99:routable', 'veth-peer:routable')
5945
5946 # checking address
5947 output = check_output('ip address show dev veth99 scope global')
5948 print(output)
5949 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5950 self.assertNotIn('192.168.5', output)
5951
5952 # checking semi-static route
5953 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5954 print(output)
5955 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
5956
5957 # Confirm that ipv6 token is not set in the kernel
5958 output = check_output('ip token show dev veth99')
5959 print(output)
5960 self.assertRegex(output, 'token :: dev veth99')
5961
5962 # Make manager and link state file updated
5963 resolvectl('revert', 'veth99')
5964
5965 # Check link state file
5966 print('## link state file')
5967 output = read_link_state_file('veth99')
5968 print(output)
5969 self.assertIn('DNS=2600::ee', output)
5970 self.assertIn('NTP=2600::ff', output)
5971
5972 # Check manager state file
5973 print('## manager state file')
5974 output = read_manager_state_file()
5975 print(output)
5976 self.assertRegex(output, 'DNS=.*2600::ee')
5977 self.assertRegex(output, 'NTP=.*2600::ff')
5978
5979 print('## dnsmasq log')
5980 output = read_dnsmasq_log_file()
5981 print(output)
5982 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
5983 self.assertIn('DHCPSOLICIT(veth-peer)', output)
5984 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
5985 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
5986 self.assertIn('DHCPREPLY(veth-peer)', output)
5987 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
5988
5989 # Check json format
5990 check_json(networkctl_json('veth99'))
5991
5992 # Testing without rapid commit support
5993 with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
5994 f.write('\n[DHCPv6]\nRapidCommit=no\n')
5995
5996 stop_dnsmasq()
5997 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5998 '--dhcp-option=option6:dns-server,[2600::ee]',
5999 '--dhcp-option=option6:ntp-server,[2600::ff]')
6000
6001 networkctl_reload()
6002 self.wait_online('veth99:routable', 'veth-peer:routable')
6003
6004 # checking address
6005 output = check_output('ip address show dev veth99 scope global')
6006 print(output)
6007 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6008 self.assertNotIn('192.168.5', output)
6009
6010 # checking semi-static route
6011 output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
6012 print(output)
6013 self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
6014
6015 # Make manager and link state file updated
6016 resolvectl('revert', 'veth99')
6017
6018 # Check link state file
6019 print('## link state file')
6020 output = read_link_state_file('veth99')
6021 print(output)
6022 self.assertIn('DNS=2600::ee', output)
6023 self.assertIn('NTP=2600::ff', output)
6024
6025 # Check manager state file
6026 print('## manager state file')
6027 output = read_manager_state_file()
6028 print(output)
6029 self.assertRegex(output, 'DNS=.*2600::ee')
6030 self.assertRegex(output, 'NTP=.*2600::ff')
6031
6032 print('## dnsmasq log')
6033 output = read_dnsmasq_log_file()
6034 print(output)
6035 self.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output)
6036 self.assertIn('DHCPSOLICIT(veth-peer)', output)
6037 self.assertIn('DHCPADVERTISE(veth-peer)', output)
6038 self.assertIn('DHCPREQUEST(veth-peer)', output)
6039 self.assertIn('DHCPREPLY(veth-peer)', output)
6040 self.assertNotIn('rapid-commit', output)
6041
6042 # Check json format
6043 check_json(networkctl_json('veth99'))
6044
6045 def test_dhcp_client_ipv6_dbus_status(self):
6046 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
6047 start_networkd()
6048 self.wait_online('veth-peer:carrier')
6049
6050 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
6051 # bit set) has yet been received and the configuration does not include WithoutRA=true
6052 state = get_dhcp6_client_state('veth99')
6053 print(f"DHCPv6 client state = {state}")
6054 self.assertEqual(state, 'stopped')
6055
6056 state = get_dhcp4_client_state('veth99')
6057 print(f"DHCPv4 client state = {state}")
6058 self.assertEqual(state, 'selecting')
6059
6060 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6061 self.wait_online('veth99:routable', 'veth-peer:routable')
6062
6063 state = get_dhcp6_client_state('veth99')
6064 print(f"DHCPv6 client state = {state}")
6065 self.assertEqual(state, 'bound')
6066
6067 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
6068 for _ in range(100):
6069 state = get_dhcp4_client_state('veth99')
6070 if state == 'stopped':
6071 break
6072 time.sleep(.2)
6073
6074 print(f"DHCPv4 client state = {state}")
6075 self.assertEqual(state, 'stopped')
6076
6077 # restart dnsmasq to clear log
6078 stop_dnsmasq()
6079 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6080
6081 # Test renew command
6082 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
6083 networkctl('renew', 'veth99')
6084
6085 for _ in range(100):
6086 state = get_dhcp4_client_state('veth99')
6087 if state == 'stopped':
6088 break
6089 time.sleep(.2)
6090
6091 print(f"DHCPv4 client state = {state}")
6092 self.assertEqual(state, 'stopped')
6093
6094 print('## dnsmasq log')
6095 output = read_dnsmasq_log_file()
6096 print(output)
6097 self.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output)
6098 self.assertIn('DHCPOFFER(veth-peer)', output)
6099 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6100 self.assertNotIn('DHCPACK(veth-peer)', output)
6101
6102 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self):
6103 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
6104
6105 start_networkd()
6106 self.wait_online('veth-peer:carrier')
6107 start_dnsmasq()
6108 self.wait_online('veth99:routable', 'veth-peer:routable')
6109
6110 # checking address
6111 output = check_output('ip address show dev veth99 scope global')
6112 print(output)
6113 self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6114 self.assertNotIn('192.168.5', output)
6115
6116 print('## dnsmasq log')
6117 output = read_dnsmasq_log_file()
6118 print(output)
6119 self.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output)
6120 self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
6121 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6122 self.assertIn('DHCPREPLY(veth-peer)', output)
6123 self.assertIn('sent size: 0 option: 14 rapid-commit', output)
6124
6125 def test_dhcp_client_ipv4_only(self):
6126 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6127
6128 self.setup_nftset('addr4', 'ipv4_addr')
6129 self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6130 self.setup_nftset('ifindex', 'iface_index')
6131
6132 start_networkd()
6133 self.wait_online('veth-peer:carrier')
6134 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6135 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
6136 '--dhcp-option=option:domain-search,example.com',
6137 '--dhcp-alternate-port=67,5555',
6138 ipv4_range='192.168.5.110,192.168.5.119')
6139 self.wait_online('veth99:routable', 'veth-peer:routable')
6140 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6141
6142 print('## ip address show dev veth99 scope global')
6143 output = check_output('ip address show dev veth99 scope global')
6144 print(output)
6145 self.assertIn('mtu 1492', output)
6146 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6147 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')
6148 self.assertNotIn('2600::', output)
6149
6150 output = check_output('ip -4 --json address show dev veth99')
6151 for i in json.loads(output)[0]['addr_info']:
6152 if i['label'] == 'test-label':
6153 address1 = i['local']
6154 break
6155 else:
6156 self.assertFalse(True)
6157
6158 self.assertRegex(address1, r'^192.168.5.11[0-9]$')
6159
6160 print('## ip route show table main dev veth99')
6161 output = check_output('ip route show table main dev veth99')
6162 print(output)
6163 # no DHCP routes assigned to the main table
6164 self.assertNotIn('proto dhcp', output)
6165 # static routes
6166 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6167 self.assertIn('192.168.5.0/24 proto static scope link', output)
6168 self.assertIn('192.168.6.0/24 proto static scope link', output)
6169 self.assertIn('192.168.7.0/24 proto static scope link', output)
6170
6171 print('## ip route show table 211 dev veth99')
6172 output = check_output('ip route show table 211 dev veth99')
6173 print(output)
6174 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6175 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6176 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6177 self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6178 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6179 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6180
6181 print('## link state file')
6182 output = read_link_state_file('veth99')
6183 print(output)
6184 # checking DNS server, SIP server, and Domains
6185 self.assertIn('DNS=192.168.5.6 192.168.5.7', output)
6186 self.assertIn('SIP=192.168.5.21 192.168.5.22', output)
6187 self.assertIn('DOMAINS=example.com', output)
6188
6189 print('## json')
6190 j = json.loads(networkctl_json('veth99'))
6191
6192 self.assertEqual(len(j['DNS']), 2)
6193 for i in j['DNS']:
6194 print(i)
6195 self.assertEqual(i['Family'], 2)
6196 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6197 self.assertRegex(a, '^192.168.5.[67]$')
6198 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6199 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6200 self.assertEqual('192.168.5.1', a)
6201
6202 self.assertEqual(len(j['SIP']), 2)
6203 for i in j['SIP']:
6204 print(i)
6205 self.assertEqual(i['Family'], 2)
6206 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6207 self.assertRegex(a, '^192.168.5.2[12]$')
6208 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6209 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6210 self.assertEqual('192.168.5.1', a)
6211
6212 print('## dnsmasq log')
6213 output = read_dnsmasq_log_file()
6214 print(output)
6215 self.assertIn('vendor class: FooBarVendorTest', output)
6216 self.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output)
6217 self.assertIn('client provides name: test-hostname', output)
6218 self.assertIn('26:mtu', output)
6219
6220 # change address range, DNS servers, and Domains
6221 stop_dnsmasq()
6222 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6223 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6224 '--dhcp-option=option:domain-search,foo.example.com',
6225 '--dhcp-alternate-port=67,5555',
6226 ipv4_range='192.168.5.120,192.168.5.129',)
6227
6228 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6229 print('Wait for the DHCP lease to be expired')
6230 self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
6231 self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
6232
6233 self.wait_online('veth99:routable', 'veth-peer:routable')
6234
6235 print('## ip address show dev veth99 scope global')
6236 output = check_output('ip address show dev veth99 scope global')
6237 print(output)
6238 self.assertIn('mtu 1492', output)
6239 self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output)
6240 self.assertNotIn(f'{address1}', output)
6241 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')
6242 self.assertNotIn('2600::', output)
6243
6244 output = check_output('ip -4 --json address show dev veth99')
6245 for i in json.loads(output)[0]['addr_info']:
6246 if i['label'] == 'test-label':
6247 address2 = i['local']
6248 break
6249 else:
6250 self.assertFalse(True)
6251
6252 self.assertRegex(address2, r'^192.168.5.12[0-9]$')
6253
6254 print('## ip route show table main dev veth99')
6255 output = check_output('ip route show table main dev veth99')
6256 print(output)
6257 # no DHCP routes assigned to the main table
6258 self.assertNotIn('proto dhcp', output)
6259 # static routes
6260 self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output)
6261 self.assertIn('192.168.5.0/24 proto static scope link', output)
6262 self.assertIn('192.168.6.0/24 proto static scope link', output)
6263 self.assertIn('192.168.7.0/24 proto static scope link', output)
6264
6265 print('## ip route show table 211 dev veth99')
6266 output = check_output('ip route show table 211 dev veth99')
6267 print(output)
6268 self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6269 self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6270 self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6271 self.assertNotIn('192.168.5.6', output)
6272 self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6273 self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6274 self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
6275
6276 print('## link state file')
6277 output = read_link_state_file('veth99')
6278 print(output)
6279 # checking DNS server, SIP server, and Domains
6280 self.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output)
6281 self.assertIn('SIP=192.168.5.23 192.168.5.24', output)
6282 self.assertIn('DOMAINS=foo.example.com', output)
6283
6284 print('## json')
6285 j = json.loads(networkctl_json('veth99'))
6286
6287 self.assertEqual(len(j['DNS']), 3)
6288 for i in j['DNS']:
6289 print(i)
6290 self.assertEqual(i['Family'], 2)
6291 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6292 self.assertRegex(a, '^192.168.5.[178]$')
6293 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6294 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6295 self.assertEqual('192.168.5.1', a)
6296
6297 self.assertEqual(len(j['SIP']), 2)
6298 for i in j['SIP']:
6299 print(i)
6300 self.assertEqual(i['Family'], 2)
6301 a = socket.inet_ntop(socket.AF_INET, bytearray(i['Address']))
6302 self.assertRegex(a, '^192.168.5.2[34]$')
6303 self.assertEqual(i['ConfigSource'], 'DHCPv4')
6304 a = socket.inet_ntop(socket.AF_INET, bytearray(i['ConfigProvider']))
6305 self.assertEqual('192.168.5.1', a)
6306
6307 print('## dnsmasq log')
6308 output = read_dnsmasq_log_file()
6309 print(output)
6310 self.assertIn('vendor class: FooBarVendorTest', output)
6311 self.assertIn(f'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output)
6312 self.assertIn('client provides name: test-hostname', output)
6313 self.assertIn('26:mtu', output)
6314
6315 self.check_netlabel('veth99', r'192\.168\.5\.0/24')
6316
6317 self.check_nftset('addr4', r'192\.168\.5\.1')
6318 self.check_nftset('network4', r'192\.168\.5\.0/24')
6319 self.check_nftset('ifindex', 'veth99')
6320
6321 self.teardown_nftset('addr4', 'network4', 'ifindex')
6322
6323 def test_dhcp_client_ipv4_dbus_status(self):
6324 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6325 start_networkd()
6326 self.wait_online('veth-peer:carrier')
6327
6328 state = get_dhcp4_client_state('veth99')
6329 print(f"State = {state}")
6330 self.assertEqual(state, 'rebooting')
6331
6332 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6333 '--dhcp-option=option:domain-search,example.com',
6334 '--dhcp-alternate-port=67,5555',
6335 ipv4_range='192.168.5.110,192.168.5.119')
6336 self.wait_online('veth99:routable', 'veth-peer:routable')
6337 self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
6338
6339 state = get_dhcp4_client_state('veth99')
6340 print(f"State = {state}")
6341 self.assertEqual(state, 'bound')
6342
6343 def test_dhcp_client_allow_list(self):
6344 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
6345
6346 start_networkd()
6347 self.wait_online('veth-peer:carrier')
6348 since = datetime.datetime.now()
6349 start_dnsmasq()
6350
6351 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6352 for _ in range(20):
6353 if expect in read_networkd_log(since=since):
6354 break
6355 time.sleep(0.5)
6356 else:
6357 self.fail()
6358
6359 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6360 since = datetime.datetime.now()
6361 networkctl_reload()
6362
6363 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6364 for _ in range(20):
6365 if expect in read_networkd_log(since=since):
6366 break
6367 time.sleep(0.5)
6368 else:
6369 self.fail()
6370
6371 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6372 since = datetime.datetime.now()
6373 networkctl_reload()
6374
6375 expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6376 for _ in range(20):
6377 if expect in read_networkd_log(since=since):
6378 break
6379 time.sleep(0.5)
6380 else:
6381 self.fail()
6382
6383 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
6384 def test_dhcp_client_rapid_commit(self):
6385 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6386 start_networkd()
6387 self.wait_online('veth-peer:carrier')
6388
6389 start_dnsmasq('--dhcp-rapid-commit')
6390 self.wait_online('veth99:routable', 'veth-peer:routable')
6391 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6392
6393 state = get_dhcp4_client_state('veth99')
6394 print(f"DHCPv4 client state = {state}")
6395 self.assertEqual(state, 'bound')
6396
6397 output = read_dnsmasq_log_file()
6398 self.assertIn('DHCPDISCOVER(veth-peer)', output)
6399 self.assertNotIn('DHCPOFFER(veth-peer)', output)
6400 self.assertNotIn('DHCPREQUEST(veth-peer)', output)
6401 self.assertIn('DHCPACK(veth-peer)', output)
6402
6403 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self):
6404 copy_network_unit('25-veth.netdev',
6405 '25-dhcp-server-ipv6-only-mode.network',
6406 '25-dhcp-client-ipv6-only-mode.network')
6407 start_networkd()
6408 self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
6409 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
6410
6411 state = get_dhcp4_client_state('veth99')
6412 print(f"State = {state}")
6413 self.assertEqual(state, 'bound')
6414
6415 def test_dhcp_client_ipv4_use_routes_gateway(self):
6416 first = True
6417 for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
6418 if first:
6419 first = False
6420 else:
6421 self.tearDown()
6422
6423 print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6424 with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
6425 self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
6426
6427 def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
6428 testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6429 testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
6430 testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
6431 testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
6432 testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6433 copy_network_unit(*testunits, copy_dropins=False)
6434
6435 start_networkd()
6436 self.wait_online('veth-peer:carrier')
6437 additional_options = [
6438 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6439 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6440 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6441 ]
6442 if classless:
6443 additional_options += [
6444 '--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'
6445 ]
6446 start_dnsmasq(*additional_options)
6447 self.wait_online('veth99:routable', 'veth-peer:routable')
6448
6449 output = check_output('ip -4 route show dev veth99')
6450 print(output)
6451
6452 # Check UseRoutes=
6453 if use_routes:
6454 if classless:
6455 self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6456 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6457 self.assertRegex(output, r'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6458 self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6459 self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6460 else:
6461 self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6462 self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6463 self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6464 self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6465 else:
6466 self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6467 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6468 self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6469 self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6470 self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6471 self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6472 self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6473 self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6474
6475 # Check UseGateway=
6476 if use_gateway and (not classless or not use_routes):
6477 self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6478 else:
6479 self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6480
6481 # Check route to gateway
6482 if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
6483 self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6484 else:
6485 self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6486
6487 # Check RoutesToDNS= and RoutesToNTP=
6488 if dns_and_ntp_routes:
6489 self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6490 self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6491 if use_routes:
6492 if classless:
6493 self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6494 self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6495 else:
6496 self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6497 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6498 else:
6499 self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6500 self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6501 else:
6502 self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6503 self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6504 self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6505 self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6506
6507 check_json(networkctl_json())
6508
6509 def test_dhcp_client_settings_anonymize(self):
6510 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6511 start_networkd()
6512 self.wait_online('veth-peer:carrier')
6513 start_dnsmasq()
6514 self.wait_online('veth99:routable', 'veth-peer:routable')
6515
6516 print('## dnsmasq log')
6517 output = read_dnsmasq_log_file()
6518 print(output)
6519 self.assertNotIn('VendorClassIdentifier=SusantVendorTest', output)
6520 self.assertNotIn('test-hostname', output)
6521 self.assertNotIn('26:mtu', output)
6522
6523 def test_dhcp_keep_configuration_dhcp(self):
6524 copy_network_unit('25-veth.netdev',
6525 '25-dhcp-server-veth-peer.network',
6526 '25-dhcp-client-keep-configuration-dhcp.network')
6527 start_networkd()
6528 self.wait_online('veth-peer:carrier')
6529 start_dnsmasq()
6530 self.wait_online('veth99:routable', 'veth-peer:routable')
6531
6532 output = check_output('ip address show dev veth99 scope global')
6533 print(output)
6534 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6535 'valid_lft forever preferred_lft forever')
6536
6537 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6538 stop_dnsmasq()
6539
6540 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6541 print('Wait for the DHCP lease to be expired')
6542 time.sleep(120)
6543
6544 # The lease address should be kept after the lease expired
6545 output = check_output('ip address show dev veth99 scope global')
6546 print(output)
6547 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6548 'valid_lft forever preferred_lft forever')
6549
6550 stop_networkd()
6551
6552 # The lease address should be kept after networkd stopped
6553 output = check_output('ip address show dev veth99 scope global')
6554 print(output)
6555 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6556 'valid_lft forever preferred_lft forever')
6557
6558 with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
6559 f.write('[Network]\nDHCP=no\n')
6560
6561 start_networkd()
6562 self.wait_online('veth99:routable', 'veth-peer:routable')
6563
6564 # Still the lease address should be kept after networkd restarted
6565 output = check_output('ip address show dev veth99 scope global')
6566 print(output)
6567 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6568 'valid_lft forever preferred_lft forever')
6569
6570 def test_dhcp_keep_configuration_dhcp_on_stop(self):
6571 copy_network_unit('25-veth.netdev',
6572 '25-dhcp-server-veth-peer.network',
6573 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6574 start_networkd()
6575 self.wait_online('veth-peer:carrier')
6576 start_dnsmasq()
6577 self.wait_online('veth99:routable', 'veth-peer:routable')
6578
6579 output = check_output('ip address show dev veth99 scope global')
6580 print(output)
6581 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6582
6583 stop_dnsmasq()
6584 stop_networkd()
6585
6586 output = check_output('ip address show dev veth99 scope global')
6587 print(output)
6588 self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6589
6590 start_networkd()
6591 self.wait_online('veth-peer:routable')
6592
6593 output = check_output('ip address show dev veth99 scope global')
6594 print(output)
6595 self.assertNotIn('192.168.5.', output)
6596
6597 def test_dhcp_client_reuse_address_as_static(self):
6598 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6599 start_networkd()
6600 self.wait_online('veth-peer:carrier')
6601 start_dnsmasq()
6602 self.wait_online('veth99:routable', 'veth-peer:routable')
6603
6604 # link become 'routable' when at least one protocol provide an valid address.
6605 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6606 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6607
6608 output = check_output('ip address show dev veth99 scope global')
6609 ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group()
6610 ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group()
6611 static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address])
6612 print(static_network)
6613
6614 remove_network_unit('25-dhcp-client.network')
6615
6616 with open(os.path.join(network_unit_dir, '25-static.network'), mode='w', encoding='utf-8') as f:
6617 f.write(static_network)
6618
6619 restart_networkd()
6620 self.wait_online('veth99:routable')
6621
6622 output = check_output('ip -4 address show dev veth99 scope global')
6623 print(output)
6624 self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6625 'valid_lft forever preferred_lft forever')
6626
6627 output = check_output('ip -6 address show dev veth99 scope global')
6628 print(output)
6629 self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *'
6630 'valid_lft forever preferred_lft forever')
6631
6632 @expectedFailureIfModuleIsNotAvailable('vrf')
6633 def test_dhcp_client_vrf(self):
6634 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6635 '25-vrf.netdev', '25-vrf.network')
6636 start_networkd()
6637 self.wait_online('veth-peer:carrier')
6638 start_dnsmasq()
6639 self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6640
6641 # link become 'routable' when at least one protocol provide an valid address.
6642 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6643 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6644
6645 print('## ip -d link show dev vrf99')
6646 output = check_output('ip -d link show dev vrf99')
6647 print(output)
6648 self.assertRegex(output, 'vrf table 42')
6649
6650 print('## ip address show vrf vrf99')
6651 output = check_output('ip address show vrf vrf99')
6652 print(output)
6653 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6654 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6655 self.assertRegex(output, 'inet6 .* scope link')
6656
6657 print('## ip address show dev veth99')
6658 output = check_output('ip address show dev veth99')
6659 print(output)
6660 self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6661 self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6662 self.assertRegex(output, 'inet6 .* scope link')
6663
6664 print('## ip route show vrf vrf99')
6665 output = check_output('ip route show vrf vrf99')
6666 print(output)
6667 self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6668 self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6669 self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6670
6671 print('## ip route show table main dev veth99')
6672 output = check_output('ip route show table main dev veth99')
6673 print(output)
6674 self.assertEqual(output, '')
6675
6676 def test_dhcp_client_gateway_onlink_implicit(self):
6677 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6678 '25-dhcp-client-gateway-onlink-implicit.network')
6679 start_networkd()
6680 self.wait_online('veth-peer:carrier')
6681 start_dnsmasq()
6682 self.wait_online('veth99:routable', 'veth-peer:routable')
6683
6684 output = networkctl_status('veth99')
6685 print(output)
6686 self.assertRegex(output, '192.168.5')
6687
6688 output = check_output('ip route list dev veth99 10.0.0.0/8')
6689 print(output)
6690 self.assertRegex(output, 'onlink')
6691 output = check_output('ip route list dev veth99 192.168.100.0/24')
6692 print(output)
6693 self.assertRegex(output, 'onlink')
6694
6695 def test_dhcp_client_with_ipv4ll(self):
6696 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6697 '25-dhcp-client-with-ipv4ll.network')
6698 start_networkd()
6699 # we need to increase timeout above default, as this will need to wait for
6700 # systemd-networkd to get the dhcpv4 transient failure event
6701 self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
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 start_dnsmasq()
6709 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6710 self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
6711 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')
6712 self.wait_online('veth99:routable')
6713
6714 output = check_output('ip -4 address show dev veth99')
6715 print(output)
6716 self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6717 self.assertNotIn('169.254.', output)
6718 self.assertNotIn('scope link', output)
6719
6720 stop_dnsmasq()
6721 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6722 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)
6723 self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
6724
6725 output = check_output('ip -4 address show dev veth99')
6726 print(output)
6727 self.assertNotIn('192.168.5.', output)
6728 self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
6729
6730 def test_dhcp_client_use_dns(self):
6731 def check(self, ipv4, ipv6):
6732 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6733 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6734 f.write('[DHCPv4]\nUseDNS=')
6735 f.write('yes' if ipv4 else 'no')
6736 f.write('\n[DHCPv6]\nUseDNS=')
6737 f.write('yes' if ipv6 else 'no')
6738 f.write('\n[IPv6AcceptRA]\nUseDNS=no')
6739
6740 networkctl_reload()
6741 self.wait_online('veth99:routable')
6742
6743 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6744 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6745 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6746
6747 # make resolved re-read the link state file
6748 resolvectl('revert', 'veth99')
6749
6750 output = resolvectl('dns', 'veth99')
6751 print(output)
6752 if ipv4:
6753 self.assertIn('192.168.5.1', output)
6754 else:
6755 self.assertNotIn('192.168.5.1', output)
6756 if ipv6:
6757 self.assertIn('2600::1', output)
6758 else:
6759 self.assertNotIn('2600::1', output)
6760
6761 check_json(networkctl_json())
6762
6763 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6764
6765 start_networkd()
6766 self.wait_online('veth-peer:carrier')
6767 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6768 '--dhcp-option=option6:dns-server,[2600::1]')
6769
6770 check(self, True, True)
6771 check(self, True, False)
6772 check(self, False, True)
6773 check(self, False, False)
6774
6775 def test_dhcp_client_use_captive_portal(self):
6776 def check(self, ipv4, ipv6):
6777 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6778 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6779 f.write('[DHCPv4]\nUseCaptivePortal=')
6780 f.write('yes' if ipv4 else 'no')
6781 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6782 f.write('yes' if ipv6 else 'no')
6783 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6784
6785 networkctl_reload()
6786 self.wait_online('veth99:routable')
6787
6788 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6789 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6790 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6791
6792 output = networkctl_status('veth99')
6793 print(output)
6794 if ipv4 or ipv6:
6795 self.assertIn('Captive Portal: http://systemd.io', output)
6796 else:
6797 self.assertNotIn('Captive Portal: http://systemd.io', output)
6798
6799 check_json(networkctl_json())
6800
6801 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6802
6803 start_networkd()
6804 self.wait_online('veth-peer:carrier')
6805 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6806 '--dhcp-option=option6:103,http://systemd.io')
6807
6808 check(self, True, True)
6809 check(self, True, False)
6810 check(self, False, True)
6811 check(self, False, False)
6812
6813 def test_dhcp_client_reject_captive_portal(self):
6814 def check(self, ipv4, ipv6):
6815 os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
6816 with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
6817 f.write('[DHCPv4]\nUseCaptivePortal=')
6818 f.write('yes' if ipv4 else 'no')
6819 f.write('\n[DHCPv6]\nUseCaptivePortal=')
6820 f.write('yes' if ipv6 else 'no')
6821 f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6822
6823 networkctl_reload()
6824 self.wait_online('veth99:routable')
6825
6826 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6827 self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
6828 self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
6829
6830 output = networkctl_status('veth99')
6831 print(output)
6832 self.assertNotIn('Captive Portal: ', output)
6833 self.assertNotIn('invalid/url', output)
6834
6835 check_json(networkctl_json())
6836
6837 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
6838
6839 start_networkd()
6840 self.wait_online('veth-peer:carrier')
6841 masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
6842 start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
6843 '--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
6844
6845 check(self, True, True)
6846 check(self, True, False)
6847 check(self, False, True)
6848 check(self, False, False)
6849
6850 class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
6851
6852 def setUp(self):
6853 setup_common()
6854
6855 def tearDown(self):
6856 tear_down_common()
6857
6858 def check_dhcp6_prefix(self, link):
6859 description = get_link_description(link)
6860
6861 self.assertIn('DHCPv6Client', description.keys())
6862 self.assertIn('Prefixes', description['DHCPv6Client'])
6863
6864 prefixInfo = description['DHCPv6Client']['Prefixes']
6865
6866 self.assertEqual(len(prefixInfo), 1)
6867
6868 self.assertIn('Prefix', prefixInfo[0].keys())
6869 self.assertIn('PrefixLength', prefixInfo[0].keys())
6870 self.assertIn('PreferredLifetimeUSec', prefixInfo[0].keys())
6871 self.assertIn('ValidLifetimeUSec', prefixInfo[0].keys())
6872
6873 self.assertEqual(prefixInfo[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6874 self.assertEqual(prefixInfo[0]['PrefixLength'], 56)
6875 self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0)
6876 self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0)
6877
6878 def test_dhcp6pd_no_address(self):
6879 # For issue #29979.
6880 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6881
6882 start_networkd()
6883 self.wait_online('veth-peer:routable')
6884 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
6885 self.wait_online('veth99:degraded')
6886
6887 print('### ip -6 address show dev veth99 scope global')
6888 output = check_output('ip -6 address show dev veth99 scope global')
6889 print(output)
6890 self.assertNotIn('inet6 3ffe:501:ffff', output)
6891
6892 self.check_dhcp6_prefix('veth99')
6893
6894 def test_dhcp6pd_no_assign(self):
6895 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6896 # However, the server does not provide IA_NA. For issue #31349.
6897 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6898
6899 start_networkd()
6900 self.wait_online('veth-peer:routable')
6901 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
6902 self.wait_online('veth99:degraded')
6903
6904 print('### ip -6 address show dev veth99 scope global')
6905 output = check_output('ip -6 address show dev veth99 scope global')
6906 print(output)
6907 self.assertNotIn('inet6 3ffe:501:ffff', output)
6908
6909 self.check_dhcp6_prefix('veth99')
6910
6911 def test_dhcp6pd(self):
6912 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6913 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6914 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6915 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6916 '25-dhcp-pd-downstream-dummy97.network',
6917 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6918 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6919
6920 start_networkd()
6921 self.wait_online('veth-peer:routable')
6922 start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
6923 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6924 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6925
6926 self.setup_nftset('addr6', 'ipv6_addr')
6927 self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6928 self.setup_nftset('ifindex', 'iface_index')
6929
6930 # Check DBus assigned prefix information to veth99
6931 self.check_dhcp6_prefix('veth99')
6932
6933 print('### ip -6 address show dev veth-peer scope global')
6934 output = check_output('ip -6 address show dev veth-peer scope global')
6935 print(output)
6936 self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
6937
6938 # Link Subnet IDs
6939 # test1: 0x00
6940 # dummy97: 0x01 (The link will appear later)
6941 # dummy98: 0x00
6942 # dummy99: auto -> 0x02 (No address assignment)
6943 # veth97: 0x08
6944 # veth98: 0x09
6945 # veth99: 0x10
6946
6947 print('### ip -6 address show dev veth99 scope global')
6948 output = check_output('ip -6 address show dev veth99 scope global')
6949 print(output)
6950 # IA_NA
6951 self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6952 # address in IA_PD (Token=static)
6953 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6954 # address in IA_PD (Token=eui64)
6955 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6956 # address in IA_PD (temporary)
6957 # Note that the temporary addresses may appear after the link enters configured state
6958 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')
6959
6960 print('### ip -6 address show dev test1 scope global')
6961 output = check_output('ip -6 address show dev test1 scope global')
6962 print(output)
6963 # address in IA_PD (Token=static)
6964 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6965 # address in IA_PD (temporary)
6966 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')
6967
6968 print('### ip -6 address show dev dummy98 scope global')
6969 output = check_output('ip -6 address show dev dummy98 scope global')
6970 print(output)
6971 # address in IA_PD (Token=static)
6972 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6973 # address in IA_PD (temporary)
6974 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')
6975
6976 print('### ip -6 address show dev dummy99 scope global')
6977 output = check_output('ip -6 address show dev dummy99 scope global')
6978 print(output)
6979 # Assign=no
6980 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6981
6982 print('### ip -6 address show dev veth97 scope global')
6983 output = check_output('ip -6 address show dev veth97 scope global')
6984 print(output)
6985 # address in IA_PD (Token=static)
6986 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6987 # address in IA_PD (Token=eui64)
6988 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6989 # address in IA_PD (temporary)
6990 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')
6991
6992 print('### ip -6 address show dev veth97-peer scope global')
6993 output = check_output('ip -6 address show dev veth97-peer scope global')
6994 print(output)
6995 # NDisc address (Token=static)
6996 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6997 # NDisc address (Token=eui64)
6998 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6999 # NDisc address (temporary)
7000 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')
7001
7002 print('### ip -6 address show dev veth98 scope global')
7003 output = check_output('ip -6 address show dev veth98 scope global')
7004 print(output)
7005 # address in IA_PD (Token=static)
7006 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7007 # address in IA_PD (Token=eui64)
7008 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7009 # address in IA_PD (temporary)
7010 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')
7011
7012 print('### ip -6 address show dev veth98-peer scope global')
7013 output = check_output('ip -6 address show dev veth98-peer scope global')
7014 print(output)
7015 # NDisc address (Token=static)
7016 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7017 # NDisc address (Token=eui64)
7018 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7019 # NDisc address (temporary)
7020 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')
7021
7022 print('### ip -6 route show type unreachable')
7023 output = check_output('ip -6 route show type unreachable')
7024 print(output)
7025 self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
7026
7027 print('### ip -6 route show dev veth99')
7028 output = check_output('ip -6 route show dev veth99')
7029 print(output)
7030 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
7031
7032 print('### ip -6 route show dev test1')
7033 output = check_output('ip -6 route show dev test1')
7034 print(output)
7035 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7036
7037 print('### ip -6 route show dev dummy98')
7038 output = check_output('ip -6 route show dev dummy98')
7039 print(output)
7040 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7041
7042 print('### ip -6 route show dev dummy99')
7043 output = check_output('ip -6 route show dev dummy99')
7044 print(output)
7045 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
7046
7047 print('### ip -6 route show dev veth97')
7048 output = check_output('ip -6 route show dev veth97')
7049 print(output)
7050 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
7051
7052 print('### ip -6 route show dev veth97-peer')
7053 output = check_output('ip -6 route show dev veth97-peer')
7054 print(output)
7055 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
7056
7057 print('### ip -6 route show dev veth98')
7058 output = check_output('ip -6 route show dev veth98')
7059 print(output)
7060 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
7061
7062 print('### ip -6 route show dev veth98-peer')
7063 output = check_output('ip -6 route show dev veth98-peer')
7064 print(output)
7065 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
7066
7067 # Test case for a downstream which appears later
7068 check_output('ip link add dummy97 type dummy')
7069 self.wait_online('dummy97:routable')
7070
7071 print('### ip -6 address show dev dummy97 scope global')
7072 output = check_output('ip -6 address show dev dummy97 scope global')
7073 print(output)
7074 # address in IA_PD (Token=static)
7075 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7076 # address in IA_PD (temporary)
7077 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')
7078
7079 print('### ip -6 route show dev dummy97')
7080 output = check_output('ip -6 route show dev dummy97')
7081 print(output)
7082 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
7083
7084 # Test case for reconfigure
7085 networkctl_reconfigure('dummy98', 'dummy99')
7086 self.wait_online('dummy98:routable', 'dummy99:degraded')
7087
7088 print('### ip -6 address show dev dummy98 scope global')
7089 output = check_output('ip -6 address show dev dummy98 scope global')
7090 print(output)
7091 # address in IA_PD (Token=static)
7092 self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7093 # address in IA_PD (temporary)
7094 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')
7095
7096 print('### ip -6 address show dev dummy99 scope global')
7097 output = check_output('ip -6 address show dev dummy99 scope global')
7098 print(output)
7099 # Assign=no
7100 self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
7101
7102 print('### ip -6 route show dev dummy98')
7103 output = check_output('ip -6 route show dev dummy98')
7104 print(output)
7105 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7106
7107 print('### ip -6 route show dev dummy99')
7108 output = check_output('ip -6 route show dev dummy99')
7109 print(output)
7110 self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
7111
7112 self.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
7113
7114 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
7115 self.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
7116 self.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
7117 self.check_nftset('ifindex', 'dummy98')
7118
7119 self.teardown_nftset('addr6', 'network6', 'ifindex')
7120
7121 def verify_dhcp4_6rd(self, tunnel_name):
7122 print('### ip -4 address show dev veth-peer scope global')
7123 output = check_output('ip -4 address show dev veth-peer scope global')
7124 print(output)
7125 self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
7126
7127 # Link Subnet IDs
7128 # test1: 0x00
7129 # dummy97: 0x01 (The link will appear later)
7130 # dummy98: 0x00
7131 # dummy99: auto -> 0x0[23] (No address assignment)
7132 # 6rd-XXX: auto -> 0x0[23]
7133 # veth97: 0x08
7134 # veth98: 0x09
7135 # veth99: 0x10
7136
7137 print('### ip -4 address show dev veth99 scope global')
7138 output = check_output('ip -4 address show dev veth99 scope global')
7139 print(output)
7140 self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7141
7142 print('### ip -6 address show dev veth99 scope global')
7143 output = check_output('ip -6 address show dev veth99 scope global')
7144 print(output)
7145 # address in IA_PD (Token=static)
7146 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7147 # address in IA_PD (Token=eui64)
7148 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7149 # address in IA_PD (temporary)
7150 # Note that the temporary addresses may appear after the link enters configured state
7151 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')
7152
7153 print('### ip -6 address show dev test1 scope global')
7154 output = check_output('ip -6 address show dev test1 scope global')
7155 print(output)
7156 # address in IA_PD (Token=static)
7157 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7158 # address in IA_PD (temporary)
7159 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')
7160
7161 print('### ip -6 address show dev dummy98 scope global')
7162 output = check_output('ip -6 address show dev dummy98 scope global')
7163 print(output)
7164 # address in IA_PD (Token=static)
7165 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7166 # address in IA_PD (temporary)
7167 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')
7168
7169 print('### ip -6 address show dev dummy99 scope global')
7170 output = check_output('ip -6 address show dev dummy99 scope global')
7171 print(output)
7172 # Assign=no
7173 self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7174
7175 print('### ip -6 address show dev veth97 scope global')
7176 output = check_output('ip -6 address show dev veth97 scope global')
7177 print(output)
7178 # address in IA_PD (Token=static)
7179 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7180 # address in IA_PD (Token=eui64)
7181 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7182 # address in IA_PD (temporary)
7183 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')
7184
7185 print('### ip -6 address show dev veth97-peer scope global')
7186 output = check_output('ip -6 address show dev veth97-peer scope global')
7187 print(output)
7188 # NDisc address (Token=static)
7189 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7190 # NDisc address (Token=eui64)
7191 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7192 # NDisc address (temporary)
7193 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')
7194
7195 print('### ip -6 address show dev veth98 scope global')
7196 output = check_output('ip -6 address show dev veth98 scope global')
7197 print(output)
7198 # address in IA_PD (Token=static)
7199 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7200 # address in IA_PD (Token=eui64)
7201 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7202 # address in IA_PD (temporary)
7203 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')
7204
7205 print('### ip -6 address show dev veth98-peer scope global')
7206 output = check_output('ip -6 address show dev veth98-peer scope global')
7207 print(output)
7208 # NDisc address (Token=static)
7209 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7210 # NDisc address (Token=eui64)
7211 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7212 # NDisc address (temporary)
7213 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')
7214
7215 print('### ip -6 route show type unreachable')
7216 output = check_output('ip -6 route show type unreachable')
7217 print(output)
7218 self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7219
7220 print('### ip -6 route show dev veth99')
7221 output = check_output('ip -6 route show dev veth99')
7222 print(output)
7223 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7224
7225 print('### ip -6 route show dev test1')
7226 output = check_output('ip -6 route show dev test1')
7227 print(output)
7228 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7229
7230 print('### ip -6 route show dev dummy98')
7231 output = check_output('ip -6 route show dev dummy98')
7232 print(output)
7233 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7234
7235 print('### ip -6 route show dev dummy99')
7236 output = check_output('ip -6 route show dev dummy99')
7237 print(output)
7238 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7239
7240 print('### ip -6 route show dev veth97')
7241 output = check_output('ip -6 route show dev veth97')
7242 print(output)
7243 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7244
7245 print('### ip -6 route show dev veth97-peer')
7246 output = check_output('ip -6 route show dev veth97-peer')
7247 print(output)
7248 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7249
7250 print('### ip -6 route show dev veth98')
7251 output = check_output('ip -6 route show dev veth98')
7252 print(output)
7253 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7254
7255 print('### ip -6 route show dev veth98-peer')
7256 output = check_output('ip -6 route show dev veth98-peer')
7257 print(output)
7258 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7259
7260 print('### ip -6 address show dev dummy97 scope global')
7261 output = check_output('ip -6 address show dev dummy97 scope global')
7262 print(output)
7263 # address in IA_PD (Token=static)
7264 self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7265 # address in IA_PD (temporary)
7266 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')
7267
7268 print('### ip -6 route show dev dummy97')
7269 output = check_output('ip -6 route show dev dummy97')
7270 print(output)
7271 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7272
7273 print(f'### ip -d link show dev {tunnel_name}')
7274 output = check_output(f'ip -d link show dev {tunnel_name}')
7275 print(output)
7276 self.assertIn('link/sit 10.100.100.', output)
7277 self.assertIn('local 10.100.100.', output)
7278 self.assertIn('ttl 64', output)
7279 self.assertIn('6rd-prefix 2001:db8::/32', output)
7280 self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
7281
7282 print(f'### ip -6 address show dev {tunnel_name}')
7283 output = check_output(f'ip -6 address show dev {tunnel_name}')
7284 print(output)
7285 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')
7286 self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7287
7288 print(f'### ip -6 route show dev {tunnel_name}')
7289 output = check_output(f'ip -6 route show dev {tunnel_name}')
7290 print(output)
7291 self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7292 self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
7293
7294 print('### ip -6 route show default')
7295 output = check_output('ip -6 route show default')
7296 print(output)
7297 self.assertIn('default', output)
7298 self.assertIn(f'via ::10.0.0.1 dev {tunnel_name}', output)
7299
7300 def test_dhcp4_6rd(self):
7301 def get_dhcp_6rd_prefix(link):
7302 description = get_link_description(link)
7303
7304 self.assertIn('DHCPv4Client', description.keys())
7305 self.assertIn('6rdPrefix', description['DHCPv4Client'].keys())
7306
7307 prefixInfo = description['DHCPv4Client']['6rdPrefix']
7308 self.assertIn('Prefix', prefixInfo.keys())
7309 self.assertIn('PrefixLength', prefixInfo.keys())
7310 self.assertIn('IPv4MaskLength', prefixInfo.keys())
7311 self.assertIn('BorderRouters', prefixInfo.keys())
7312
7313 return prefixInfo
7314
7315 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7316 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7317 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7318 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7319 '25-dhcp-pd-downstream-dummy97.network',
7320 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7321 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7322 '80-6rd-tunnel.network')
7323
7324 start_networkd()
7325 self.wait_online('veth-peer:routable')
7326
7327 # ipv4masklen: 8
7328 # 6rd-prefix: 2001:db8::/32
7329 # br-addresss: 10.0.0.1
7330
7331 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',
7332 ipv4_range='10.100.100.100,10.100.100.200',
7333 ipv4_router='10.0.0.1')
7334 self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7335 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7336
7337 # Check the DBus interface for assigned prefix information
7338 prefixInfo = get_dhcp_6rd_prefix('veth99')
7339
7340 self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7341 self.assertEqual(prefixInfo['PrefixLength'], 32)
7342 self.assertEqual(prefixInfo['IPv4MaskLength'], 8)
7343 self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]])
7344
7345 # Test case for a downstream which appears later
7346 check_output('ip link add dummy97 type dummy')
7347 self.wait_online('dummy97:routable')
7348
7349 # Find tunnel name
7350 tunnel_name = None
7351 for name in os.listdir('/sys/class/net/'):
7352 if name.startswith('6rd-'):
7353 tunnel_name = name
7354 break
7355
7356 self.wait_online(f'{tunnel_name}:routable')
7357
7358 self.verify_dhcp4_6rd(tunnel_name)
7359
7360 # Test case for reconfigure
7361 networkctl_reconfigure('dummy98', 'dummy99')
7362 self.wait_online('dummy98:routable', 'dummy99:degraded')
7363
7364 self.verify_dhcp4_6rd(tunnel_name)
7365
7366 print('Wait for the DHCP lease to be renewed/rebind')
7367 time.sleep(120)
7368
7369 self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7370 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7371
7372 self.verify_dhcp4_6rd(tunnel_name)
7373
7374 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
7375
7376 def setUp(self):
7377 setup_common()
7378
7379 def tearDown(self):
7380 tear_down_common()
7381
7382 def test_ipv6_route_prefix(self):
7383 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7384 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7385
7386 start_networkd()
7387 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7388
7389 output = check_output('ip address show dev veth-peer')
7390 print(output)
7391 self.assertIn('inet6 2001:db8:0:1:', output)
7392 self.assertNotIn('inet6 2001:db8:0:2:', output)
7393 self.assertNotIn('inet6 2001:db8:0:3:', output)
7394
7395 output = check_output('ip -6 route show dev veth-peer')
7396 print(output)
7397 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7398 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7399 self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
7400 self.assertIn('2001:db0:fff::/64 via ', output)
7401 self.assertNotIn('2001:db1:fff::/64 via ', output)
7402 self.assertNotIn('2001:db2:fff::/64 via ', output)
7403
7404 output = check_output('ip address show dev veth99')
7405 print(output)
7406 self.assertNotIn('inet6 2001:db8:0:1:', output)
7407 self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
7408 self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
7409 self.assertNotIn('inet6 2001:db8:0:3:', output)
7410
7411 output = resolvectl('dns', 'veth-peer')
7412 print(output)
7413 self.assertRegex(output, '2001:db8:1:1::2')
7414
7415 output = resolvectl('domain', 'veth-peer')
7416 print(output)
7417 self.assertIn('example.com', output)
7418
7419 check_json(networkctl_json())
7420
7421 output = networkctl_json('veth-peer')
7422 check_json(output)
7423
7424 # PREF64 or NAT64
7425 pref64 = json.loads(output)['NDisc']['PREF64'][0]
7426
7427 prefix = socket.inet_ntop(socket.AF_INET6, bytearray(pref64['Prefix']))
7428 self.assertEqual(prefix, '64:ff9b::')
7429
7430 prefix_length = pref64['PrefixLength']
7431 self.assertEqual(prefix_length, 96)
7432
7433 def test_ipv6_route_prefix_deny_list(self):
7434 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7435 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7436
7437 start_networkd()
7438 self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7439
7440 output = check_output('ip address show dev veth-peer')
7441 print(output)
7442 self.assertIn('inet6 2001:db8:0:1:', output)
7443 self.assertNotIn('inet6 2001:db8:0:2:', output)
7444
7445 output = check_output('ip -6 route show dev veth-peer')
7446 print(output)
7447 self.assertIn('2001:db8:0:1::/64 proto ra', output)
7448 self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
7449 self.assertIn('2001:db0:fff::/64 via ', output)
7450 self.assertNotIn('2001:db1:fff::/64 via ', output)
7451
7452 output = check_output('ip address show dev veth99')
7453 print(output)
7454 self.assertNotIn('inet6 2001:db8:0:1:', output)
7455 self.assertIn('inet6 2001:db8:0:2:', output)
7456
7457 output = resolvectl('dns', 'veth-peer')
7458 print(output)
7459 self.assertRegex(output, '2001:db8:1:1::2')
7460
7461 output = resolvectl('domain', 'veth-peer')
7462 print(output)
7463 self.assertIn('example.com', output)
7464
7465 class NetworkdMTUTests(unittest.TestCase, Utilities):
7466
7467 def setUp(self):
7468 setup_common()
7469
7470 def tearDown(self):
7471 tear_down_common()
7472
7473 def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
7474 if not ipv6_mtu:
7475 ipv6_mtu = mtu
7476
7477 # test normal start
7478 start_networkd()
7479 self.wait_online('dummy98:routable')
7480 self.check_link_attr('dummy98', 'mtu', mtu)
7481 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7482
7483 # test normal restart
7484 restart_networkd()
7485 self.wait_online('dummy98:routable')
7486 self.check_link_attr('dummy98', 'mtu', mtu)
7487 self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
7488
7489 if reset:
7490 self.reset_check_mtu(mtu, ipv6_mtu)
7491
7492 def reset_check_mtu(self, mtu, ipv6_mtu=None):
7493 ''' test setting mtu/ipv6_mtu with interface already up '''
7494 stop_networkd()
7495
7496 # note - changing the device mtu resets the ipv6 mtu
7497 check_output('ip link set up mtu 1501 dev dummy98')
7498 check_output('ip link set up mtu 1500 dev dummy98')
7499 self.check_link_attr('dummy98', 'mtu', '1500')
7500 self.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7501
7502 self.check_mtu(mtu, ipv6_mtu, reset=False)
7503
7504 def test_mtu_network(self):
7505 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7506 self.check_mtu('1600')
7507
7508 def test_mtu_netdev(self):
7509 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins=False)
7510 # note - MTU set by .netdev happens ONLY at device creation!
7511 self.check_mtu('1600', reset=False)
7512
7513 def test_mtu_link(self):
7514 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
7515 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7516 self.check_mtu('1600', reset=False)
7517
7518 def test_ipv6_mtu(self):
7519 ''' set ipv6 mtu without setting device mtu '''
7520 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7521 self.check_mtu('1500', '1400')
7522
7523 def test_ipv6_mtu_toolarge(self):
7524 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7525 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7526 self.check_mtu('1500', '1500')
7527
7528 def test_mtu_network_ipv6_mtu(self):
7529 ''' set ipv6 mtu and set device mtu via network file '''
7530 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7531 self.check_mtu('1600', '1550')
7532
7533 def test_mtu_netdev_ipv6_mtu(self):
7534 ''' set ipv6 mtu and set device mtu via netdev file '''
7535 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7536 self.check_mtu('1600', '1550', reset=False)
7537
7538 def test_mtu_link_ipv6_mtu(self):
7539 ''' set ipv6 mtu and set device mtu via link file '''
7540 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7541 self.check_mtu('1600', '1550', reset=False)
7542
7543
7544 if __name__ == '__main__':
7545 parser = argparse.ArgumentParser()
7546 parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
7547 parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
7548 parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
7549 parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
7550 parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
7551 parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
7552 parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
7553 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)
7554 ns, unknown_args = parser.parse_known_args(namespace=unittest)
7555
7556 if ns.build_dir:
7557 networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
7558 resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
7559 timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
7560 wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
7561 networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
7562 resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
7563 timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
7564 udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
7565 build_dir = ns.build_dir
7566
7567 if ns.source_dir:
7568 source_dir = ns.source_dir
7569 else:
7570 source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
7571 assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
7572
7573 use_valgrind = ns.use_valgrind
7574 enable_debug = ns.enable_debug
7575 asan_options = ns.asan_options
7576 lsan_options = ns.lsan_options
7577 ubsan_options = ns.ubsan_options
7578 with_coverage = ns.with_coverage
7579
7580 if use_valgrind:
7581 # Do not forget the trailing space.
7582 valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7583
7584 networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
7585 resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
7586 timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
7587 udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
7588 wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
7589
7590 if asan_options:
7591 env.update({'ASAN_OPTIONS': asan_options})
7592 if lsan_options:
7593 env.update({'LSAN_OPTIONS': lsan_options})
7594 if ubsan_options:
7595 env.update({'UBSAN_OPTIONS': ubsan_options})
7596 if use_valgrind:
7597 env.update({'SYSTEMD_MEMPOOL': '0'})
7598
7599 wait_online_env = env.copy()
7600 if enable_debug:
7601 wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7602
7603 sys.argv[1:] = unknown_args
7604 unittest.main(verbosity=3)