1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3# pylint: disable=line-too-long,too-many-lines,too-many-branches,too-many-statements,too-many-arguments
4# pylint: disable=too-many-public-methods,too-many-boolean-expressions,invalid-name
5# pylint: disable=missing-function-docstring,missing-class-docstring,missing-module-docstring
6# systemd-networkd tests
7
8# These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
9# simply run this file which can be found in the VM at /root/src/test/test-network/systemd-networkd-tests.py.
10
11import argparse
12import errno
13import itertools
14import os
15import re
16import shutil
17import signal
18import subprocess
19import sys
20import time
21import unittest
22from shutil import copytree
23from pathlib import Path
24
25network_unit_file_path='/run/systemd/network'
26networkd_runtime_directory='/run/systemd/netif'
27networkd_conf_dropin_path='/run/systemd/networkd.conf.d'
28networkd_ci_path='/run/networkd-ci'
29network_sysctl_ipv6_path='/proc/sys/net/ipv6/conf'
30network_sysctl_ipv4_path='/proc/sys/net/ipv4/conf'
31
32udev_rules_dir='/run/udev/rules.d'
33
34dnsmasq_pid_file='/run/networkd-ci/test-dnsmasq.pid'
35dnsmasq_log_file='/run/networkd-ci/test-dnsmasq.log'
36dnsmasq_lease_file='/run/networkd-ci/test-dnsmasq.lease'
37
38isc_dhcpd_pid_file='/run/networkd-ci/test-isc-dhcpd.pid'
39isc_dhcpd_lease_file='/run/networkd-ci/test-isc-dhcpd.lease'
40
41systemd_lib_paths=['/usr/lib/systemd', '/lib/systemd']
42which_paths=':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
43
44networkd_bin=shutil.which('systemd-networkd', path=which_paths)
45resolved_bin=shutil.which('systemd-resolved', path=which_paths)
46udevd_bin=shutil.which('systemd-udevd', path=which_paths)
47wait_online_bin=shutil.which('systemd-networkd-wait-online', path=which_paths)
48networkctl_bin=shutil.which('networkctl', path=which_paths)
49resolvectl_bin=shutil.which('resolvectl', path=which_paths)
50timedatectl_bin=shutil.which('timedatectl', path=which_paths)
51
52use_valgrind=False
53enable_debug=True
54env = {}
55asan_options=None
56lsan_options=None
57ubsan_options=None
58with_coverage=False
59
60running_units = []
61
62def check_output(*command, **kwargs):
63    # This replaces both check_output and check_call (output can be ignored)
64    command = command[0].split() + list(command[1:])
65    return subprocess.check_output(command, universal_newlines=True, **kwargs).rstrip()
66
67def call(*command, **kwargs):
68    command = command[0].split() + list(command[1:])
69    return subprocess.call(command, universal_newlines=True, **kwargs)
70
71def run(*command, **kwargs):
72    command = command[0].split() + list(command[1:])
73    return subprocess.run(command, universal_newlines=True, check=False, **kwargs)
74
75def is_module_available(module_name):
76    lsmod_output = check_output('lsmod')
77    module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
78    return module_re.search(lsmod_output) or not call('modprobe', module_name, stderr=subprocess.DEVNULL)
79
80def expectedFailureIfModuleIsNotAvailable(module_name):
81    def f(func):
82        if not is_module_available(module_name):
83            return unittest.expectedFailure(func)
84        return func
85
86    return f
87
88def expectedFailureIfERSPANModuleIsNotAvailable():
89    def f(func):
90        rc = call('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 1 erspan 123', stderr=subprocess.DEVNULL)
91        if rc == 0:
92            call('ip link del erspan99')
93            return func
94
95        return unittest.expectedFailure(func)
96
97    return f
98
99def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
100    def f(func):
101        rc = call('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7', stderr=subprocess.DEVNULL)
102        if rc == 0:
103            call('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
104            return func
105
106        return unittest.expectedFailure(func)
107
108    return f
109
110def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
111    def f(func):
112        rc = call('ip rule add not from 192.168.100.19 ipproto tcp table 7', stderr=subprocess.DEVNULL)
113        if rc == 0:
114            call('ip rule del not from 192.168.100.19 ipproto tcp table 7')
115            return func
116
117        return unittest.expectedFailure(func)
118
119    return f
120
121def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
122    def f(func):
123        support = False
124        rc = call('ip rule add from 192.168.100.19 table 7 uidrange 200-300', stderr=subprocess.DEVNULL)
125        if rc == 0:
126            ret = run('ip rule list from 192.168.100.19 table 7', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
127            if ret.returncode == 0 and 'uidrange 200-300' in ret.stdout.rstrip():
128                support = True
129            call('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
130
131        if support:
132            return func
133
134        return unittest.expectedFailure(func)
135
136    return f
137
138def expectedFailureIfLinkFileFieldIsNotSet():
139    def f(func):
140        support = False
141        rc = call('ip link add name dummy99 type dummy', stderr=subprocess.DEVNULL)
142        if rc == 0:
143            ret = run('udevadm info -w10s /sys/class/net/dummy99', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
144            if ret.returncode == 0 and 'E: ID_NET_LINK_FILE=' in ret.stdout.rstrip():
145                support = True
146            call('ip link del dummy99')
147
148        if support:
149            return func
150
151        return unittest.expectedFailure(func)
152
153    return f
154
155def expectedFailureIfNexthopIsNotAvailable():
156    def f(func):
157        rc = call('ip nexthop list', stderr=subprocess.DEVNULL)
158        if rc == 0:
159            return func
160
161        return unittest.expectedFailure(func)
162
163    return f
164
165def expectedFailureIfRTA_VIAIsNotSupported():
166    def f(func):
167        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
168        call('ip link set up dev dummy98', stderr=subprocess.DEVNULL)
169        call('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98', stderr=subprocess.DEVNULL)
170        rc = call('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98', stderr=subprocess.DEVNULL)
171        call('ip link del dummy98', stderr=subprocess.DEVNULL)
172        if rc == 0:
173            return func
174
175        return unittest.expectedFailure(func)
176
177    return f
178
179def expectedFailureIfAlternativeNameIsNotAvailable():
180    def f(func):
181        supported = False
182        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
183        rc = call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr=subprocess.DEVNULL)
184        if rc == 0:
185            rc = call('ip link show dev hogehogehogehogehoge', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
186            if rc == 0:
187                supported = True
188
189        call('ip link del dummy98', stderr=subprocess.DEVNULL)
190        if supported:
191            return func
192
193        return unittest.expectedFailure(func)
194
195    return f
196
197def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
198    def f(func):
199        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
200        rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL)
201        if rc != 0:
202            return unittest.expectedFailure(func)
203
204        try:
205            with open('/sys/bus/netdevsim/new_device', mode='w') as f:
206                f.write('99 1')
207        except OSError:
208            return unittest.expectedFailure(func)
209
210        call('udevadm settle')
211        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
212        try:
213            with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
214                f.write('3')
215        except OSError:
216            call('rmmod netdevsim', stderr=subprocess.DEVNULL)
217            return unittest.expectedFailure(func)
218
219        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
220        return func
221
222    return f
223
224def expectedFailureIfCAKEIsNotAvailable():
225    def f(func):
226        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
227        rc = call('tc qdisc add dev dummy98 parent root cake', stderr=subprocess.DEVNULL)
228        call('ip link del dummy98', stderr=subprocess.DEVNULL)
229        if rc == 0:
230            return func
231
232        return unittest.expectedFailure(func)
233
234    return f
235
236def expectedFailureIfPIEIsNotAvailable():
237    def f(func):
238        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
239        rc = call('tc qdisc add dev dummy98 parent root pie', stderr=subprocess.DEVNULL)
240        call('ip link del dummy98', stderr=subprocess.DEVNULL)
241        if rc == 0:
242            return func
243
244        return unittest.expectedFailure(func)
245
246    return f
247
248def expectedFailureIfHHFIsNotAvailable():
249    def f(func):
250        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
251        rc = call('tc qdisc add dev dummy98 parent root hhf', stderr=subprocess.DEVNULL)
252        call('ip link del dummy98', stderr=subprocess.DEVNULL)
253        if rc == 0:
254            return func
255
256        return unittest.expectedFailure(func)
257
258    return f
259
260def expectedFailureIfETSIsNotAvailable():
261    def f(func):
262        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
263        rc = call('tc qdisc add dev dummy98 parent root ets bands 10', stderr=subprocess.DEVNULL)
264        call('ip link del dummy98', stderr=subprocess.DEVNULL)
265        if rc == 0:
266            return func
267
268        return unittest.expectedFailure(func)
269
270    return f
271
272def expectedFailureIfFQPIEIsNotAvailable():
273    def f(func):
274        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
275        rc = call('tc qdisc add dev dummy98 parent root fq_pie', stderr=subprocess.DEVNULL)
276        call('ip link del dummy98', stderr=subprocess.DEVNULL)
277        if rc == 0:
278            return func
279
280        return unittest.expectedFailure(func)
281
282    return f
283
284def setUpModule():
285    # pylint: disable=global-statement
286    global running_units
287
288    os.makedirs(network_unit_file_path, exist_ok=True)
289    os.makedirs(networkd_conf_dropin_path, exist_ok=True)
290    os.makedirs(networkd_ci_path, exist_ok=True)
291    os.makedirs(udev_rules_dir, exist_ok=True)
292
293    shutil.rmtree(networkd_ci_path)
294    copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
295
296    shutil.copy(os.path.join(networkd_ci_path, '00-debug-net.rules'), udev_rules_dir)
297
298    for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service',
299              'systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
300              'firewalld.service']:
301        if call(f'systemctl is-active --quiet {u}') == 0:
302            check_output(f'systemctl stop {u}')
303            running_units.append(u)
304
305    drop_in = [
306        '[Unit]',
307        'StartLimitIntervalSec=0',
308        '[Service]',
309        'Restart=no',
310        'ExecStart=',
311    ]
312    if use_valgrind:
313        drop_in += [
314            'ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + networkd_bin,
315            'PrivateTmp=yes'
316        ]
317    else:
318        drop_in += ['ExecStart=!!' + networkd_bin]
319    if enable_debug:
320        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
321    if asan_options:
322        drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
323    if lsan_options:
324        drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
325    if ubsan_options:
326        drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
327    if asan_options or lsan_options or ubsan_options:
328        drop_in += ['SystemCallFilter=']
329    if use_valgrind or asan_options or lsan_options or ubsan_options:
330        drop_in += ['MemoryDenyWriteExecute=no']
331    if with_coverage:
332        drop_in += [
333            'ProtectSystem=no',
334            'ProtectHome=no',
335        ]
336
337    os.makedirs('/run/systemd/system/systemd-networkd.service.d', exist_ok=True)
338    with open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode='w') as f:
339        f.write('\n'.join(drop_in))
340
341    drop_in = [
342        '[Service]',
343        'Restart=no',
344        'ExecStart=',
345    ]
346    if use_valgrind:
347        drop_in += ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin]
348    else:
349        drop_in += ['ExecStart=!!' + resolved_bin]
350    if enable_debug:
351        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
352    if asan_options:
353        drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
354    if lsan_options:
355        drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
356    if ubsan_options:
357        drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
358    if asan_options or lsan_options or ubsan_options:
359        drop_in += ['SystemCallFilter=']
360    if use_valgrind or asan_options or lsan_options or ubsan_options:
361        drop_in += ['MemoryDenyWriteExecute=no']
362    if with_coverage:
363        drop_in += [
364            'ProtectSystem=no',
365            'ProtectHome=no',
366        ]
367
368    os.makedirs('/run/systemd/system/systemd-resolved.service.d', exist_ok=True)
369    with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w') as f:
370        f.write('\n'.join(drop_in))
371
372    drop_in = [
373        '[Service]',
374        'ExecStart=',
375        'ExecStart=!!' + udevd_bin,
376    ]
377
378    os.makedirs('/run/systemd/system/systemd-udevd.service.d', exist_ok=True)
379    with open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode='w') as f:
380        f.write('\n'.join(drop_in))
381
382    check_output('systemctl daemon-reload')
383    print(check_output('systemctl cat systemd-networkd.service'))
384    print(check_output('systemctl cat systemd-resolved.service'))
385    print(check_output('systemctl cat systemd-udevd.service'))
386    check_output('systemctl restart systemd-resolved')
387    check_output('systemctl restart systemd-udevd')
388
389def tearDownModule():
390    # pylint: disable=global-statement
391    global running_units
392
393    shutil.rmtree(networkd_ci_path)
394    os.remove(os.path.join(udev_rules_dir, '00-debug-net.rules'))
395
396    for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service']:
397        check_output(f'systemctl stop {u}')
398
399    shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
400    shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
401    shutil.rmtree('/run/systemd/system/systemd-udevd.service.d')
402    check_output('systemctl daemon-reload')
403    check_output('systemctl restart systemd-udevd.service')
404
405    for u in running_units:
406        check_output(f'systemctl start {u}')
407
408def read_link_attr(*args):
409    with open(os.path.join('/sys/class/net/', *args)) as f:
410        return f.readline().strip()
411
412def read_bridge_port_attr(bridge, link, attribute):
413    path_bridge = os.path.join('/sys/devices/virtual/net', bridge)
414    path_port = 'lower_' + link + '/brport'
415    path = os.path.join(path_bridge, path_port)
416
417    with open(os.path.join(path, attribute)) as f:
418        return f.readline().strip()
419
420def link_exists(link):
421    return os.path.exists(os.path.join('/sys/class/net', link))
422
423def remove_links(links):
424    for link in links:
425        if link_exists(link):
426            call('ip link del dev', link)
427    time.sleep(1)
428
429def remove_fou_ports(ports):
430    for port in ports:
431        call('ip fou del port', port, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
432
433def remove_routing_policy_rule_tables(tables):
434    for table in tables:
435        rc = 0
436        while rc == 0:
437            rc = call('ip rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
438        rc = 0
439        while rc == 0:
440            rc = call('ip -6 rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
441
442def remove_routes(routes):
443    for route_type, addr in routes:
444        call('ip route del', route_type, addr, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
445
446def remove_blackhole_nexthops():
447    ret = run('ip nexthop show dev lo', stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
448    if ret.returncode == 0:
449        for line in ret.stdout.rstrip().splitlines():
450            dev_id = line.split()[1]
451            call(f'ip nexthop del id {dev_id}')
452
453def remove_l2tp_tunnels(tunnel_ids):
454    output = check_output('ip l2tp show tunnel')
455    for tid in tunnel_ids:
456        words='Tunnel ' + tid + ', encap'
457        if words in output:
458            call('ip l2tp del tunnel tid', tid)
459    time.sleep(1)
460
461def read_ipv6_sysctl_attr(link, attribute):
462    with open(os.path.join(os.path.join(network_sysctl_ipv6_path, link), attribute)) as f:
463        return f.readline().strip()
464
465def read_ipv4_sysctl_attr(link, attribute):
466    with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f:
467        return f.readline().strip()
468
469def copy_unit_to_networkd_unit_path(*units, dropins=True):
470    """Copy networkd unit files into the testbed.
471
472    Any networkd unit file type can be specified, as well as drop-in files.
473
474    By default, all drop-ins for a specified unit file are copied in;
475    to avoid that specify dropins=False.
476
477    When a drop-in file is specified, its unit file is also copied in automatically.
478    """
479    print()
480    for unit in units:
481        if dropins and os.path.exists(os.path.join(networkd_ci_path, unit + '.d')):
482            copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d'))
483        if unit.endswith('.conf'):
484            dropin = unit
485            dropindir = os.path.join(network_unit_file_path, os.path.dirname(dropin))
486            os.makedirs(dropindir, exist_ok=True)
487            shutil.copy(os.path.join(networkd_ci_path, dropin), dropindir)
488            unit = os.path.dirname(dropin).rstrip('.d')
489        shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path)
490
491def remove_unit_from_networkd_path(units):
492    """Remove previously copied unit files from the testbed.
493
494    Drop-ins will be removed automatically.
495    """
496    for unit in units:
497        if os.path.exists(os.path.join(network_unit_file_path, unit)):
498            os.remove(os.path.join(network_unit_file_path, unit))
499            if os.path.exists(os.path.join(network_unit_file_path, unit + '.d')):
500                shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d'))
501
502def copy_networkd_conf_dropin(*dropins):
503    """Copy networkd.conf dropin files into the testbed."""
504    for dropin in dropins:
505        shutil.copy(os.path.join(networkd_ci_path, dropin), networkd_conf_dropin_path)
506
507def remove_networkd_conf_dropin(dropins):
508    """Remove previously copied networkd.conf dropin files from the testbed."""
509    for dropin in dropins:
510        if os.path.exists(os.path.join(networkd_conf_dropin_path, dropin)):
511            os.remove(os.path.join(networkd_conf_dropin_path, dropin))
512
513def start_dnsmasq(additional_options='', interface='veth-peer', ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20', lease_time='1h'):
514    dnsmasq_command = f'dnsmasq -8 {dnsmasq_log_file} --log-queries=extra --log-dhcp --pid-file={dnsmasq_pid_file} --conf-file=/dev/null --bind-interfaces --interface={interface} --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile={dnsmasq_lease_file} --dhcp-option=26,1492 --dhcp-option=option:router,{ipv4_router} --port=0 ' + additional_options
515    check_output(dnsmasq_command)
516
517def stop_by_pid_file(pid_file):
518    if os.path.exists(pid_file):
519        with open(pid_file, 'r') as f:
520            pid = f.read().rstrip(' \t\r\n\0')
521            os.kill(int(pid), signal.SIGTERM)
522            for _ in range(25):
523                try:
524                    os.kill(int(pid), 0)
525                    print(f"PID {pid} is still alive, waiting...")
526                    time.sleep(.2)
527                except OSError as e:
528                    if e.errno == errno.ESRCH:
529                        break
530                    print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
531
532        os.remove(pid_file)
533
534def stop_dnsmasq():
535    stop_by_pid_file(dnsmasq_pid_file)
536
537def dump_dnsmasq_log_file():
538    if os.path.exists(dnsmasq_log_file):
539        with open (dnsmasq_log_file) as in_file:
540            print(in_file.read())
541
542def search_words_in_dnsmasq_log(words, show_all=False):
543    if os.path.exists(dnsmasq_log_file):
544        with open (dnsmasq_log_file) as in_file:
545            contents = in_file.read()
546            if show_all:
547                print(contents)
548            for line in contents.splitlines():
549                if words in line:
550                    in_file.close()
551                    print("%s, %s" % (words, line))
552                    return True
553    return False
554
555def remove_dnsmasq_lease_file():
556    if os.path.exists(dnsmasq_lease_file):
557        os.remove(dnsmasq_lease_file)
558
559def remove_dnsmasq_log_file():
560    if os.path.exists(dnsmasq_log_file):
561        os.remove(dnsmasq_log_file)
562
563def start_isc_dhcpd(interface, conf_file, ip):
564    conf_file_path = os.path.join(networkd_ci_path, conf_file)
565    isc_dhcpd_command = f'dhcpd {ip} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
566    Path(isc_dhcpd_lease_file).touch()
567    check_output(isc_dhcpd_command)
568
569def stop_isc_dhcpd():
570    stop_by_pid_file(isc_dhcpd_pid_file)
571    if os.path.exists(isc_dhcpd_lease_file):
572        os.remove(isc_dhcpd_lease_file)
573
574def remove_networkd_state_files():
575    if os.path.exists(os.path.join(networkd_runtime_directory, 'state')):
576        os.remove(os.path.join(networkd_runtime_directory, 'state'))
577
578def stop_networkd(show_logs=True, remove_state_files=True):
579    if show_logs:
580        invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
581    check_output('systemctl stop systemd-networkd.socket')
582    check_output('systemctl stop systemd-networkd.service')
583    if show_logs:
584        print(check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id))
585    if remove_state_files:
586        remove_networkd_state_files()
587
588def start_networkd(sleep_sec=0):
589    check_output('systemctl start systemd-networkd')
590    if sleep_sec > 0:
591        time.sleep(sleep_sec)
592
593def restart_networkd(sleep_sec=0, show_logs=True, remove_state_files=True):
594    stop_networkd(show_logs, remove_state_files)
595    start_networkd(sleep_sec)
596
597class Utilities():
598    # pylint: disable=no-member
599
600    def check_link_exists(self, link):
601        self.assertTrue(link_exists(link))
602
603    def check_link_attr(self, *args):
604        self.assertEqual(read_link_attr(*args[:-1]), args[-1])
605
606    def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
607        # wait for the interface is activated.
608        invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
609        needle = f'{link}: Bringing link {state}'
610        flag = state.upper()
611        for iteration in range(timeout+1):
612            if iteration != 0:
613                time.sleep(1)
614            if not link_exists(link):
615                continue
616            output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id)
617            if needle in output and flag in check_output(f'ip link show {link}'):
618                return True
619        if fail_assert:
620            self.fail(f'Timed out waiting for {link} activated.')
621        return False
622
623    def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
624        """Wait for the link to reach the specified operstate and/or setup state.
625
626        Specify None or '' for either operstate or setup_state to ignore that state.
627        This will recheck until the state conditions are met or the timeout expires.
628
629        If the link successfully matches the requested state, this returns True.
630        If this times out waiting for the link to match, the behavior depends on the
631        'fail_assert' parameter; if True, this causes a test assertion failure,
632        otherwise this returns False.  The default is to cause assertion failure.
633
634        Note that this function matches on *exactly* the given operstate and setup_state.
635        To wait for a link to reach *or exceed* a given operstate, use wait_online().
636        """
637        if not operstate:
638            operstate = r'\S+'
639        if not setup_state:
640            setup_state = r'\S+'
641
642        for secs in range(setup_timeout + 1):
643            if secs != 0:
644                time.sleep(1)
645            if not link_exists(link):
646                continue
647            output = check_output(*networkctl_cmd, '-n', '0', 'status', link, env=env)
648            print(output)
649            if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
650                return True
651        if fail_assert:
652            self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
653        return False
654
655    def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
656        """Wait for the link(s) to reach the specified operstate and/or setup state.
657
658        This is similar to wait_operstate() but can be used for multiple links,
659        and it also calls systemd-networkd-wait-online to wait for the given operstate.
660        The operstate should be specified in the link name, like 'eth0:degraded'.
661        If just a link name is provided, wait-online's default operstate to wait for is degraded.
662
663        The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
664        'setup_timeout' controls the per-link timeout waiting for the setup_state.
665
666        Set 'bool_any' to True to wait for any (instead of all) of the given links.
667        If this is set, no setup_state checks are done.
668
669        Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
670        This is applied only for the operational state 'degraded' or above.
671
672        Note that this function waits for the link(s) to reach *or exceed* the given operstate.
673        However, the setup_state, if specified, must be matched *exactly*.
674
675        This returns if the link(s) reached the requested operstate/setup_state; otherwise it
676        raises CalledProcessError or fails test assertion.
677        """
678        args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
679        if bool_any:
680            args += ['--any']
681        if ipv4:
682            args += ['--ipv4']
683        if ipv6:
684            args += ['--ipv6']
685        try:
686            check_output(*args, env=env)
687        except subprocess.CalledProcessError:
688            for link in links_with_operstate:
689                output = check_output(*networkctl_cmd, '-n', '0', 'status', link.split(':')[0], env=env)
690                print(output)
691            raise
692        if not bool_any and setup_state:
693            for link in links_with_operstate:
694                self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
695
696    def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
697        for i in range(timeout_sec):
698            if i > 0:
699                time.sleep(1)
700            output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
701            if re.search(address_regex, output) and 'tentative' not in output:
702                break
703
704        self.assertRegex(output, address_regex)
705
706    def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
707        for i in range(timeout_sec):
708            if i > 0:
709                time.sleep(1)
710            output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
711            if not re.search(address_regex, output):
712                break
713
714        self.assertNotRegex(output, address_regex)
715
716class NetworkctlTests(unittest.TestCase, Utilities):
717
718    links = [
719        'dummy98',
720        'test1',
721        'veth99',
722    ]
723
724    units = [
725        '11-dummy.netdev',
726        '11-dummy-mtu.netdev',
727        '11-dummy.network',
728        '12-dummy.netdev',
729        '12-dummy.link',
730        '25-address-static.network',
731        '25-veth.netdev',
732        '26-netdev-link-local-addressing-yes.network',
733    ]
734
735    def setUp(self):
736        remove_links(self.links)
737        stop_networkd(show_logs=False)
738
739    def tearDown(self):
740        remove_links(self.links)
741        remove_unit_from_networkd_path(self.units)
742        stop_networkd(show_logs=True)
743
744    @expectedFailureIfAlternativeNameIsNotAvailable()
745    def test_altname(self):
746        copy_unit_to_networkd_unit_path('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
747        check_output('udevadm control --reload')
748        start_networkd()
749        self.wait_online(['dummy98:degraded'])
750
751        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
752        self.assertRegex(output, 'hogehogehogehogehogehoge')
753
754    def test_reconfigure(self):
755        copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
756        start_networkd()
757        self.wait_online(['dummy98:routable'])
758
759        output = check_output('ip -4 address show dev dummy98')
760        print(output)
761        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
762        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
763        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
764
765        check_output('ip address del 10.1.2.3/16 dev dummy98')
766        check_output('ip address del 10.1.2.4/16 dev dummy98')
767        check_output('ip address del 10.2.2.4/16 dev dummy98')
768
769        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
770        self.wait_online(['dummy98:routable'])
771
772        output = check_output('ip -4 address show dev dummy98')
773        print(output)
774        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
775        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
776        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
777
778        remove_unit_from_networkd_path(['25-address-static.network'])
779
780        check_output(*networkctl_cmd, 'reload', env=env)
781        self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
782
783        output = check_output('ip -4 address show dev dummy98')
784        print(output)
785        self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
786        self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
787        self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
788
789        copy_unit_to_networkd_unit_path('25-address-static.network')
790        check_output(*networkctl_cmd, 'reload', env=env)
791        self.wait_online(['dummy98:routable'])
792
793        output = check_output('ip -4 address show dev dummy98')
794        print(output)
795        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
796        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
797        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
798
799    def test_reload(self):
800        start_networkd(3)
801
802        copy_unit_to_networkd_unit_path('11-dummy.netdev')
803        check_output(*networkctl_cmd, 'reload', env=env)
804        self.wait_operstate('test1', 'off', setup_state='unmanaged')
805
806        copy_unit_to_networkd_unit_path('11-dummy.network')
807        check_output(*networkctl_cmd, 'reload', env=env)
808        self.wait_online(['test1:degraded'])
809
810        remove_unit_from_networkd_path(['11-dummy.network'])
811        check_output(*networkctl_cmd, 'reload', env=env)
812        self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
813
814        remove_unit_from_networkd_path(['11-dummy.netdev'])
815        check_output(*networkctl_cmd, 'reload', env=env)
816        self.wait_operstate('test1', 'degraded', setup_state='unmanaged')
817
818        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
819        check_output(*networkctl_cmd, 'reload', env=env)
820        self.wait_operstate('test1', 'degraded')
821
822    def test_glob(self):
823        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
824        start_networkd()
825
826        self.wait_online(['test1:degraded'])
827
828        output = check_output(*networkctl_cmd, 'list', env=env)
829        self.assertRegex(output, '1 lo ')
830        self.assertRegex(output, 'test1')
831
832        output = check_output(*networkctl_cmd, 'list', 'test1', env=env)
833        self.assertNotRegex(output, '1 lo ')
834        self.assertRegex(output, 'test1')
835
836        output = check_output(*networkctl_cmd, 'list', 'te*', env=env)
837        self.assertNotRegex(output, '1 lo ')
838        self.assertRegex(output, 'test1')
839
840        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'te*', env=env)
841        self.assertNotRegex(output, '1: lo ')
842        self.assertRegex(output, 'test1')
843
844        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'tes[a-z][0-9]', env=env)
845        self.assertNotRegex(output, '1: lo ')
846        self.assertRegex(output, 'test1')
847
848    def test_mtu(self):
849        copy_unit_to_networkd_unit_path('11-dummy-mtu.netdev', '11-dummy.network')
850        start_networkd()
851
852        self.wait_online(['test1:degraded'])
853
854        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
855        self.assertRegex(output, 'MTU: 1600')
856
857    def test_type(self):
858        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
859        start_networkd()
860        self.wait_online(['test1:degraded'])
861
862        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
863        print(output)
864        self.assertRegex(output, 'Type: ether')
865
866        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
867        print(output)
868        self.assertRegex(output, 'Type: loopback')
869
870    @expectedFailureIfLinkFileFieldIsNotSet()
871    def test_udev_link_file(self):
872        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
873        start_networkd()
874        self.wait_online(['test1:degraded'])
875
876        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
877        print(output)
878        self.assertRegex(output, r'Link File: (/usr)?/lib/systemd/network/99-default.link')
879        self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
880
881        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
882        print(output)
883        self.assertRegex(output, r'Link File: n/a')
884        self.assertRegex(output, r'Network File: n/a')
885
886    def test_delete_links(self):
887        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
888                                        '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
889        start_networkd()
890
891        self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
892
893        check_output(*networkctl_cmd, 'delete', 'test1', 'veth99', env=env)
894        self.assertFalse(link_exists('test1'))
895        self.assertFalse(link_exists('veth99'))
896        self.assertFalse(link_exists('veth-peer'))
897
898class NetworkdNetDevTests(unittest.TestCase, Utilities):
899
900    links_remove_earlier = [
901        'xfrm98',
902        'xfrm99',
903    ]
904
905    links = [
906        '6rdtun99',
907        'bareudp99',
908        'batadv99',
909        'bond98',
910        'bond99',
911        'bridge99',
912        'dropin-test',
913        'dummy98',
914        'erspan98',
915        'erspan99',
916        'geneve99',
917        'gretap96',
918        'gretap98',
919        'gretap99',
920        'gretun96',
921        'gretun97',
922        'gretun98',
923        'gretun99',
924        'ifb99',
925        'ip6gretap98',
926        'ip6gretap99',
927        'ip6gretun96',
928        'ip6gretun97',
929        'ip6gretun98',
930        'ip6gretun99',
931        'ip6tnl-external',
932        'ip6tnl-slaac',
933        'ip6tnl97',
934        'ip6tnl98',
935        'ip6tnl99',
936        'ipiptun96',
937        'ipiptun97',
938        'ipiptun98',
939        'ipiptun99',
940        'ipvlan99',
941        'ipvtap99',
942        'isataptun99',
943        'macvlan99',
944        'macvtap99',
945        'nlmon99',
946        'sittun96',
947        'sittun97',
948        'sittun98',
949        'sittun99',
950        'tap99',
951        'test1',
952        'tun99',
953        'vcan99',
954        'veth-mtu',
955        'veth99',
956        'vlan99',
957        'vrf99',
958        'vti6tun97',
959        'vti6tun98',
960        'vti6tun99',
961        'vtitun96',
962        'vtitun97',
963        'vtitun98',
964        'vtitun99',
965        'vxcan99',
966        'vxlan-slaac',
967        'vxlan97',
968        'vxlan98',
969        'vxlan99',
970        'wg97',
971        'wg98',
972        'wg99',
973    ]
974
975    units = [
976        '10-dropin-test.netdev',
977        '11-dummy.netdev',
978        '11-dummy.network',
979        '12-dummy.netdev',
980        '13-not-match-udev-property.network',
981        '14-match-udev-property.network',
982        '15-name-conflict-test.netdev',
983        '21-macvlan.netdev',
984        '21-macvtap.netdev',
985        '21-vlan-test1.network',
986        '21-vlan.netdev',
987        '21-vlan.network',
988        '25-6rd-tunnel.netdev',
989        '25-bareudp.netdev',
990        '25-batadv.netdev',
991        '25-bond.netdev',
992        '25-bond-balanced-tlb.netdev',
993        '25-bridge.netdev',
994        '25-bridge-configure-without-carrier.network',
995        '25-bridge.network',
996        '25-erspan-tunnel-local-any.netdev',
997        '25-erspan-tunnel.netdev',
998        '25-fou-gretap.netdev',
999        '25-fou-gre.netdev',
1000        '25-fou-ipip.netdev',
1001        '25-fou-ipproto-gre.netdev',
1002        '25-fou-ipproto-ipip.netdev',
1003        '25-fou-sit.netdev',
1004        '25-geneve.netdev',
1005        '25-gretap-tunnel-local-any.netdev',
1006        '25-gretap-tunnel.netdev',
1007        '25-gre-tunnel-any-any.netdev',
1008        '25-gre-tunnel-local-any.netdev',
1009        '25-gre-tunnel-remote-any.netdev',
1010        '25-gre-tunnel.netdev',
1011        '25-ifb.netdev',
1012        '25-ip6gretap-tunnel-local-any.netdev',
1013        '25-ip6gretap-tunnel.netdev',
1014        '25-ip6gre-tunnel-any-any.netdev',
1015        '25-ip6gre-tunnel-local-any.netdev',
1016        '25-ip6gre-tunnel-remote-any.netdev',
1017        '25-ip6gre-tunnel.netdev',
1018        '25-ip6tnl-tunnel-external.netdev',
1019        '25-ip6tnl-tunnel-local-any.netdev',
1020        '25-ip6tnl-tunnel-local-slaac.netdev',
1021        '25-ip6tnl-tunnel-local-slaac.network',
1022        '25-ip6tnl-tunnel-remote-any.netdev',
1023        '25-ip6tnl-tunnel.netdev',
1024        '25-ipip-tunnel-any-any.netdev',
1025        '25-ipip-tunnel-independent.netdev',
1026        '25-ipip-tunnel-independent-loopback.netdev',
1027        '25-ipip-tunnel-local-any.netdev',
1028        '25-ipip-tunnel-remote-any.netdev',
1029        '25-ipip-tunnel.netdev',
1030        '25-ipvlan.netdev',
1031        '25-ipvtap.netdev',
1032        '25-isatap-tunnel.netdev',
1033        '25-macsec.key',
1034        '25-macsec.netdev',
1035        '25-macsec.network',
1036        '25-nlmon.netdev',
1037        '25-sit-tunnel-any-any.netdev',
1038        '25-sit-tunnel-local-any.netdev',
1039        '25-sit-tunnel-remote-any.netdev',
1040        '25-sit-tunnel.netdev',
1041        '25-tap.netdev',
1042        '25-tun.netdev',
1043        '25-tunnel-any-any.network',
1044        '25-tunnel-local-any.network',
1045        '25-tunnel-remote-any.network',
1046        '25-tunnel.network',
1047        '25-vcan.netdev',
1048        '25-veth-mtu.netdev',
1049        '25-veth.netdev',
1050        '25-vrf.netdev',
1051        '25-vti6-tunnel-any-any.netdev',
1052        '25-vti6-tunnel-local-any.netdev',
1053        '25-vti6-tunnel-remote-any.netdev',
1054        '25-vti6-tunnel.netdev',
1055        '25-vti-tunnel-any-any.netdev',
1056        '25-vti-tunnel-local-any.netdev',
1057        '25-vti-tunnel-remote-any.netdev',
1058        '25-vti-tunnel.netdev',
1059        '25-vxcan.netdev',
1060        '25-vxlan-independent.netdev',
1061        '25-vxlan-ipv6.netdev',
1062        '25-vxlan-local-slaac.netdev',
1063        '25-vxlan-local-slaac.network',
1064        '25-vxlan-veth99.network',
1065        '25-vxlan.netdev',
1066        '25-wireguard-23-peers.netdev',
1067        '25-wireguard-23-peers.network',
1068        '25-wireguard-no-peer.netdev',
1069        '25-wireguard-no-peer.network',
1070        '25-wireguard-preshared-key.txt',
1071        '25-wireguard-private-key.txt',
1072        '25-wireguard.netdev',
1073        '25-wireguard.network',
1074        '25-xfrm.netdev',
1075        '25-xfrm-independent.netdev',
1076        '25-6rd.network',
1077        '25-erspan.network',
1078        '25-gre.network',
1079        '25-gretap.network',
1080        '25-gretun.network',
1081        '25-ip6gretap.network',
1082        '25-ip6gretun.network',
1083        '25-ip6tnl-slaac.network',
1084        '25-ip6tnl.network',
1085        '25-ipip.network',
1086        '25-ipv6-prefix.network',
1087        '25-ipvlan.network',
1088        '25-ipvtap.network',
1089        '25-isatap.network',
1090        '26-macsec.network',
1091        '25-macvlan.network',
1092        '25-macvtap.network',
1093        '26-netdev-link-local-addressing-yes.network',
1094        '25-sit.network',
1095        '25-vti6.network',
1096        '25-vti.network',
1097        '25-vxlan-ipv6.network',
1098        '25-vxlan-test1.network',
1099        '25-vxlan.network',
1100        '25-xfrm.network',
1101    ]
1102
1103    fou_ports = [
1104        '55555',
1105        '55556']
1106
1107    def setUp(self):
1108        remove_fou_ports(self.fou_ports)
1109        remove_links(self.links_remove_earlier)
1110        remove_links(self.links)
1111        stop_networkd(show_logs=False)
1112
1113    def tearDown(self):
1114        remove_fou_ports(self.fou_ports)
1115        remove_links(self.links_remove_earlier)
1116        remove_links(self.links)
1117        remove_unit_from_networkd_path(self.units)
1118        stop_networkd(show_logs=True)
1119
1120    def test_dropin_and_name_conflict(self):
1121        copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1122        start_networkd()
1123
1124        self.wait_online(['dropin-test:off'], setup_state='unmanaged')
1125
1126        output = check_output('ip link show dropin-test')
1127        print(output)
1128        self.assertRegex(output, '00:50:56:c0:00:28')
1129
1130    def test_match_udev_property(self):
1131        copy_unit_to_networkd_unit_path('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1132        start_networkd()
1133        self.wait_online(['dummy98:routable'])
1134
1135        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
1136        print(output)
1137        self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
1138
1139    def test_wait_online_any(self):
1140        copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1141        start_networkd()
1142
1143        self.wait_online(['bridge99', 'test1:degraded'], bool_any=True)
1144
1145        self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
1146        self.wait_operstate('test1', 'degraded')
1147
1148    @expectedFailureIfModuleIsNotAvailable('bareudp')
1149    def test_bareudp(self):
1150        copy_unit_to_networkd_unit_path('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1151        start_networkd()
1152
1153        self.wait_online(['bareudp99:degraded'])
1154
1155        output = check_output('ip -d link show bareudp99')
1156        print(output)
1157        self.assertRegex(output, 'dstport 1000 ')
1158        self.assertRegex(output, 'ethertype ip ')
1159
1160    @expectedFailureIfModuleIsNotAvailable('batman-adv')
1161    def test_batadv(self):
1162        copy_unit_to_networkd_unit_path('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1163        start_networkd()
1164
1165        self.wait_online(['batadv99:degraded'])
1166
1167        output = check_output('ip -d link show batadv99')
1168        print(output)
1169        self.assertRegex(output, 'batadv')
1170
1171    def test_bridge(self):
1172        copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1173        start_networkd()
1174
1175        self.wait_online(['bridge99:no-carrier'])
1176
1177        tick = os.sysconf('SC_CLK_TCK')
1178        self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
1179        self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
1180        self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick))
1181        self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick))
1182        self.assertEqual(9,         int(read_link_attr('bridge99', 'bridge', 'priority')))
1183        self.assertEqual(1,         int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1184        self.assertEqual(1,         int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1185        self.assertEqual(1,         int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1186        self.assertEqual(3,         int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1187
1188        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env)
1189        print(output)
1190        self.assertRegex(output, 'Priority: 9')
1191        self.assertRegex(output, 'STP: yes')
1192        self.assertRegex(output, 'Multicast IGMP Version: 3')
1193
1194        output = check_output('ip -d link show bridge99')
1195        print(output)
1196        self.assertIn('vlan_filtering 1 ', output)
1197        self.assertIn('vlan_protocol 802.1ad ', output)
1198        self.assertIn('vlan_default_pvid 9 ', output)
1199
1200    def test_bond(self):
1201        copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1202        start_networkd()
1203
1204        self.wait_online(['bond99:off', 'bond98:off'], setup_state='unmanaged')
1205
1206        self.assertEqual('802.3ad 4',         read_link_attr('bond99', 'bonding', 'mode'))
1207        self.assertEqual('layer3+4 1',        read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
1208        self.assertEqual('1000',              read_link_attr('bond99', 'bonding', 'miimon'))
1209        self.assertEqual('fast 1',            read_link_attr('bond99', 'bonding', 'lacp_rate'))
1210        self.assertEqual('2000',              read_link_attr('bond99', 'bonding', 'updelay'))
1211        self.assertEqual('2000',              read_link_attr('bond99', 'bonding', 'downdelay'))
1212        self.assertEqual('4',                 read_link_attr('bond99', 'bonding', 'resend_igmp'))
1213        self.assertEqual('1',                 read_link_attr('bond99', 'bonding', 'min_links'))
1214        self.assertEqual('1218',              read_link_attr('bond99', 'bonding', 'ad_actor_sys_prio'))
1215        self.assertEqual('811',               read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
1216        self.assertEqual('00:11:22:33:44:55', read_link_attr('bond99', 'bonding', 'ad_actor_system'))
1217
1218        self.assertEqual('balance-tlb 5',     read_link_attr('bond98', 'bonding', 'mode'))
1219        self.assertEqual('1',                 read_link_attr('bond98', 'bonding', 'tlb_dynamic_lb'))
1220
1221        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bond99', env=env)
1222        print(output)
1223        self.assertIn('Mode: 802.3ad', output)
1224        self.assertIn('Miimon: 1s', output)
1225        self.assertIn('Updelay: 2s', output)
1226        self.assertIn('Downdelay: 2s', output)
1227
1228        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bond98', env=env)
1229        print(output)
1230        self.assertIn('Mode: balance-tlb', output)
1231
1232    def test_vlan(self):
1233        copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev',
1234                                        '21-vlan.network', '21-vlan-test1.network')
1235        start_networkd()
1236
1237        self.wait_online(['test1:degraded', 'vlan99:routable'])
1238
1239        output = check_output('ip -d link show test1')
1240        print(output)
1241        self.assertRegex(output, ' mtu 2000 ')
1242
1243        output = check_output('ip -d link show vlan99')
1244        print(output)
1245        self.assertRegex(output, ' mtu 2000 ')
1246        self.assertRegex(output, 'REORDER_HDR')
1247        self.assertRegex(output, 'LOOSE_BINDING')
1248        self.assertRegex(output, 'GVRP')
1249        self.assertRegex(output, 'MVRP')
1250        self.assertRegex(output, ' id 99 ')
1251
1252        output = check_output('ip -4 address show dev test1')
1253        print(output)
1254        self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1255        self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1256
1257        output = check_output('ip -4 address show dev vlan99')
1258        print(output)
1259        self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1260
1261    def test_macvtap(self):
1262        for mode in ['private', 'vepa', 'bridge', 'passthru']:
1263            with self.subTest(mode=mode):
1264                if mode != 'private':
1265                    self.tearDown()
1266                copy_unit_to_networkd_unit_path('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1267                                                '11-dummy.netdev', '25-macvtap.network')
1268                with open(os.path.join(network_unit_file_path, '21-macvtap.netdev'), mode='a') as f:
1269                    f.write('[MACVTAP]\nMode=' + mode)
1270                start_networkd()
1271
1272                self.wait_online(['macvtap99:degraded',
1273                                  'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
1274
1275                output = check_output('ip -d link show macvtap99')
1276                print(output)
1277                self.assertRegex(output, 'macvtap mode ' + mode + ' ')
1278
1279    def test_macvlan(self):
1280        for mode in ['private', 'vepa', 'bridge', 'passthru']:
1281            with self.subTest(mode=mode):
1282                if mode != 'private':
1283                    self.tearDown()
1284                copy_unit_to_networkd_unit_path('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1285                                                '11-dummy.netdev', '25-macvlan.network')
1286                with open(os.path.join(network_unit_file_path, '21-macvlan.netdev'), mode='a') as f:
1287                    f.write('[MACVLAN]\nMode=' + mode)
1288                start_networkd()
1289
1290                self.wait_online(['macvlan99:degraded',
1291                                  'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
1292
1293                output = check_output('ip -d link show test1')
1294                print(output)
1295                self.assertRegex(output, ' mtu 2000 ')
1296
1297                output = check_output('ip -d link show macvlan99')
1298                print(output)
1299                self.assertRegex(output, ' mtu 2000 ')
1300                self.assertRegex(output, 'macvlan mode ' + mode + ' ')
1301
1302                rc = call("ip link del test1")
1303                self.assertEqual(rc, 0)
1304                time.sleep(1)
1305
1306                rc = call("ip link add test1 type dummy")
1307                self.assertEqual(rc, 0)
1308                time.sleep(1)
1309
1310                self.wait_online(['macvlan99:degraded',
1311                                  'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
1312
1313                output = check_output('ip -d link show test1')
1314                print(output)
1315                self.assertRegex(output, ' mtu 2000 ')
1316
1317                output = check_output('ip -d link show macvlan99')
1318                print(output)
1319                self.assertRegex(output, ' mtu 2000 ')
1320                self.assertRegex(output, 'macvlan mode ' + mode + ' ')
1321
1322    @expectedFailureIfModuleIsNotAvailable('ipvlan')
1323    def test_ipvlan(self):
1324        for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1325            with self.subTest(mode=mode, flag=flag):
1326                if mode != 'L2':
1327                    self.tearDown()
1328                copy_unit_to_networkd_unit_path('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1329                                                '11-dummy.netdev', '25-ipvlan.network')
1330                with open(os.path.join(network_unit_file_path, '25-ipvlan.netdev'), mode='a') as f:
1331                    f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
1332
1333                start_networkd()
1334                self.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1335
1336                output = check_output('ip -d link show ipvlan99')
1337                print(output)
1338                self.assertRegex(output, 'ipvlan  *mode ' + mode.lower() + ' ' + flag)
1339
1340    @expectedFailureIfModuleIsNotAvailable('ipvtap')
1341    def test_ipvtap(self):
1342        for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1343            with self.subTest(mode=mode, flag=flag):
1344                if mode != 'L2':
1345                    self.tearDown()
1346                copy_unit_to_networkd_unit_path('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1347                                                '11-dummy.netdev', '25-ipvtap.network')
1348                with open(os.path.join(network_unit_file_path, '25-ipvtap.netdev'), mode='a') as f:
1349                    f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
1350
1351                start_networkd()
1352                self.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1353
1354                output = check_output('ip -d link show ipvtap99')
1355                print(output)
1356                self.assertRegex(output, 'ipvtap  *mode ' + mode.lower() + ' ' + flag)
1357
1358    def test_veth(self):
1359        copy_unit_to_networkd_unit_path('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1360                                        '25-veth-mtu.netdev')
1361        start_networkd()
1362
1363        self.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
1364
1365        output = check_output('ip -d link show veth99')
1366        print(output)
1367        self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
1368        output = check_output('ip -d link show veth-peer')
1369        print(output)
1370        self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
1371
1372        output = check_output('ip -d link show veth-mtu')
1373        print(output)
1374        self.assertRegex(output, 'link/ether 12:34:56:78:9a:be')
1375        self.assertRegex(output, 'mtu 1800')
1376        output = check_output('ip -d link show veth-mtu-peer')
1377        print(output)
1378        self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
1379        self.assertRegex(output, 'mtu 1800')
1380
1381    def test_tun(self):
1382        copy_unit_to_networkd_unit_path('25-tun.netdev')
1383        start_networkd()
1384
1385        self.wait_online(['tun99:off'], setup_state='unmanaged')
1386
1387        output = check_output('ip -d link show tun99')
1388        print(output)
1389        # Old ip command does not support IFF_ flags
1390        self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1391
1392    def test_tap(self):
1393        copy_unit_to_networkd_unit_path('25-tap.netdev')
1394        start_networkd()
1395
1396        self.wait_online(['tap99:off'], setup_state='unmanaged')
1397
1398        output = check_output('ip -d link show tap99')
1399        print(output)
1400        # Old ip command does not support IFF_ flags
1401        self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1402
1403    @expectedFailureIfModuleIsNotAvailable('vrf')
1404    def test_vrf(self):
1405        copy_unit_to_networkd_unit_path('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1406        start_networkd()
1407
1408        self.wait_online(['vrf99:carrier'])
1409
1410    @expectedFailureIfModuleIsNotAvailable('vcan')
1411    def test_vcan(self):
1412        copy_unit_to_networkd_unit_path('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network')
1413        start_networkd()
1414
1415        self.wait_online(['vcan99:carrier'])
1416
1417    @expectedFailureIfModuleIsNotAvailable('vxcan')
1418    def test_vxcan(self):
1419        copy_unit_to_networkd_unit_path('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1420        start_networkd()
1421
1422        self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1423
1424    @expectedFailureIfModuleIsNotAvailable('wireguard')
1425    def test_wireguard(self):
1426        copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
1427                                        '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1428                                        '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1429                                        '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1430        start_networkd()
1431        self.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
1432
1433        output = check_output('ip -4 address show dev wg99')
1434        print(output)
1435        self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
1436
1437        output = check_output('ip -4 address show dev wg98')
1438        print(output)
1439        self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
1440
1441        output = check_output('ip -6 address show dev wg98')
1442        print(output)
1443        self.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output)
1444
1445        output = check_output('ip -4 route show dev wg99 table 1234')
1446        print(output)
1447        self.assertIn('192.168.26.0/24 proto static metric 123', output)
1448
1449        output = check_output('ip -6 route show dev wg99 table 1234')
1450        print(output)
1451        self.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output)
1452
1453        output = check_output('ip -6 route show dev wg98 table 1234')
1454        print(output)
1455        self.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output)
1456        self.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output)
1457        self.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output)
1458        self.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output)
1459        self.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output)
1460        self.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output)
1461        self.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output)
1462        self.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output)
1463        self.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output)
1464        self.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output)
1465        self.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output)
1466        self.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output)
1467        self.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output)
1468        self.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output)
1469        self.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output)
1470        self.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output)
1471        self.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output)
1472        self.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output)
1473        self.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output)
1474        self.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output)
1475        self.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output)
1476        self.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output)
1477        self.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output)
1478        self.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output)
1479        self.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output)
1480        self.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output)
1481        self.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output)
1482        self.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output)
1483        self.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output)
1484        self.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output)
1485        self.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output)
1486        self.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output)
1487        self.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output)
1488        self.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output)
1489        self.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output)
1490        self.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output)
1491        self.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output)
1492        self.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output)
1493        self.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output)
1494        self.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output)
1495        self.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output)
1496        self.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output)
1497        self.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output)
1498        self.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output)
1499        self.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output)
1500        self.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output)
1501
1502        if shutil.which('wg'):
1503            call('wg')
1504
1505            output = check_output('wg show wg99 listen-port')
1506            self.assertEqual(output, '51820')
1507            output = check_output('wg show wg99 fwmark')
1508            self.assertEqual(output, '0x4d2')
1509            output = check_output('wg show wg99 private-key')
1510            self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1511            output = check_output('wg show wg99 allowed-ips')
1512            self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output)
1513            self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output)
1514            self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output)
1515            self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output)
1516            output = check_output('wg show wg99 persistent-keepalive')
1517            self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output)
1518            self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output)
1519            self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output)
1520            self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output)
1521            output = check_output('wg show wg99 endpoints')
1522            self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output)
1523            self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output)
1524            self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output)
1525            self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output)
1526            output = check_output('wg show wg99 preshared-keys')
1527            self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output)
1528            self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output)
1529            self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output)
1530            self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output)
1531
1532            output = check_output('wg show wg98 private-key')
1533            self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1534
1535            output = check_output('wg show wg97 listen-port')
1536            self.assertEqual(output, '51821')
1537            output = check_output('wg show wg97 fwmark')
1538            self.assertEqual(output, '0x4d3')
1539
1540    def test_geneve(self):
1541        copy_unit_to_networkd_unit_path('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1542        start_networkd()
1543
1544        self.wait_online(['geneve99:degraded'])
1545
1546        output = check_output('ip -d link show geneve99')
1547        print(output)
1548        self.assertRegex(output, '192.168.22.1')
1549        self.assertRegex(output, '6082')
1550        self.assertRegex(output, 'udpcsum')
1551        self.assertRegex(output, 'udp6zerocsumrx')
1552
1553    def test_ipip_tunnel(self):
1554        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ipip.network',
1555                                        '25-ipip-tunnel.netdev', '25-tunnel.network',
1556                                        '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1557                                        '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1558                                        '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1559        start_networkd()
1560        self.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1561
1562        output = check_output('ip -d link show ipiptun99')
1563        print(output)
1564        self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1565        output = check_output('ip -d link show ipiptun98')
1566        print(output)
1567        self.assertRegex(output, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1568        output = check_output('ip -d link show ipiptun97')
1569        print(output)
1570        self.assertRegex(output, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1571        output = check_output('ip -d link show ipiptun96')
1572        print(output)
1573        self.assertRegex(output, 'ipip (ipip )?remote any local any dev dummy98')
1574
1575    def test_gre_tunnel(self):
1576        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretun.network',
1577                                        '25-gre-tunnel.netdev', '25-tunnel.network',
1578                                        '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1579                                        '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1580                                        '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1581        start_networkd()
1582        self.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1583
1584        output = check_output('ip -d link show gretun99')
1585        print(output)
1586        self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1587        self.assertRegex(output, 'ikey 1.2.3.103')
1588        self.assertRegex(output, 'okey 1.2.4.103')
1589        self.assertRegex(output, 'iseq')
1590        self.assertRegex(output, 'oseq')
1591        output = check_output('ip -d link show gretun98')
1592        print(output)
1593        self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
1594        self.assertRegex(output, 'ikey 0.0.0.104')
1595        self.assertRegex(output, 'okey 0.0.0.104')
1596        self.assertNotRegex(output, 'iseq')
1597        self.assertNotRegex(output, 'oseq')
1598        output = check_output('ip -d link show gretun97')
1599        print(output)
1600        self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
1601        self.assertRegex(output, 'ikey 0.0.0.105')
1602        self.assertRegex(output, 'okey 0.0.0.105')
1603        self.assertNotRegex(output, 'iseq')
1604        self.assertNotRegex(output, 'oseq')
1605        output = check_output('ip -d link show gretun96')
1606        print(output)
1607        self.assertRegex(output, 'gre remote any local any dev dummy98')
1608        self.assertRegex(output, 'ikey 0.0.0.106')
1609        self.assertRegex(output, 'okey 0.0.0.106')
1610        self.assertNotRegex(output, 'iseq')
1611        self.assertNotRegex(output, 'oseq')
1612
1613    def test_ip6gre_tunnel(self):
1614        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gretun.network',
1615                                        '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1616                                        '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1617                                        '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1618                                        '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1619        start_networkd(5)
1620
1621        # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1622
1623        self.check_link_exists('dummy98')
1624        self.check_link_exists('ip6gretun99')
1625        self.check_link_exists('ip6gretun98')
1626        self.check_link_exists('ip6gretun97')
1627        self.check_link_exists('ip6gretun96')
1628
1629        output = check_output('ip -d link show ip6gretun99')
1630        print(output)
1631        self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1632        output = check_output('ip -d link show ip6gretun98')
1633        print(output)
1634        self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1635        output = check_output('ip -d link show ip6gretun97')
1636        print(output)
1637        self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1638        output = check_output('ip -d link show ip6gretun96')
1639        print(output)
1640        self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
1641
1642    def test_gretap_tunnel(self):
1643        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretap.network',
1644                                        '25-gretap-tunnel.netdev', '25-tunnel.network',
1645                                        '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1646        start_networkd()
1647        self.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1648
1649        output = check_output('ip -d link show gretap99')
1650        print(output)
1651        self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1652        self.assertRegex(output, 'ikey 0.0.0.106')
1653        self.assertRegex(output, 'okey 0.0.0.106')
1654        self.assertRegex(output, 'iseq')
1655        self.assertRegex(output, 'oseq')
1656        output = check_output('ip -d link show gretap98')
1657        print(output)
1658        self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
1659        self.assertRegex(output, 'ikey 0.0.0.107')
1660        self.assertRegex(output, 'okey 0.0.0.107')
1661        self.assertRegex(output, 'iseq')
1662        self.assertRegex(output, 'oseq')
1663
1664    def test_ip6gretap_tunnel(self):
1665        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gretap.network',
1666                                        '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
1667                                        '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1668        start_networkd()
1669        self.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
1670
1671        output = check_output('ip -d link show ip6gretap99')
1672        print(output)
1673        self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1674        output = check_output('ip -d link show ip6gretap98')
1675        print(output)
1676        self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
1677
1678    def test_vti_tunnel(self):
1679        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti.network',
1680                                        '25-vti-tunnel.netdev', '25-tunnel.network',
1681                                        '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1682                                        '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1683                                        '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1684        start_networkd()
1685        self.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
1686
1687        output = check_output('ip -d link show vtitun99')
1688        print(output)
1689        self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1690        output = check_output('ip -d link show vtitun98')
1691        print(output)
1692        self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
1693        output = check_output('ip -d link show vtitun97')
1694        print(output)
1695        self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
1696        output = check_output('ip -d link show vtitun96')
1697        print(output)
1698        self.assertRegex(output, 'vti remote any local any dev dummy98')
1699
1700    def test_vti6_tunnel(self):
1701        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti6.network',
1702                                        '25-vti6-tunnel.netdev', '25-tunnel.network',
1703                                        '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1704                                        '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
1705        start_networkd()
1706        self.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
1707
1708        output = check_output('ip -d link show vti6tun99')
1709        print(output)
1710        self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1711        output = check_output('ip -d link show vti6tun98')
1712        print(output)
1713        self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
1714        output = check_output('ip -d link show vti6tun97')
1715        print(output)
1716        self.assertRegex(output, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1717
1718    def test_ip6tnl_tunnel(self):
1719        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6tnl.network',
1720                                        '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
1721                                        '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1722                                        '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1723                                        '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
1724                                        '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
1725                                        '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
1726        start_networkd()
1727        self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
1728                          'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
1729                          'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
1730
1731        output = check_output('ip -d link show ip6tnl99')
1732        print(output)
1733        self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
1734        output = check_output('ip -d link show ip6tnl98')
1735        print(output)
1736        self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
1737        output = check_output('ip -d link show ip6tnl97')
1738        print(output)
1739        self.assertRegex(output, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1740        output = check_output('ip -d link show ip6tnl-external')
1741        print(output)
1742        self.assertIn('ip6tnl-external@NONE:', output)
1743        self.assertIn('ip6tnl external ', output)
1744        output = check_output('ip -d link show ip6tnl-slaac')
1745        print(output)
1746        self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
1747
1748        output = check_output('ip -6 address show veth99')
1749        print(output)
1750        self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
1751
1752        output = check_output('ip -4 route show default')
1753        print(output)
1754        self.assertIn('default dev ip6tnl-slaac proto static', output)
1755
1756    def test_sit_tunnel(self):
1757        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-sit.network',
1758                                        '25-sit-tunnel.netdev', '25-tunnel.network',
1759                                        '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1760                                        '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1761                                        '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1762        start_networkd()
1763        self.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
1764
1765        output = check_output('ip -d link show sittun99')
1766        print(output)
1767        self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
1768        output = check_output('ip -d link show sittun98')
1769        print(output)
1770        self.assertRegex(output, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
1771        output = check_output('ip -d link show sittun97')
1772        print(output)
1773        self.assertRegex(output, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
1774        output = check_output('ip -d link show sittun96')
1775        print(output)
1776        self.assertRegex(output, "sit (ip6ip )?remote any local any dev dummy98")
1777
1778    def test_isatap_tunnel(self):
1779        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-isatap.network',
1780                                        '25-isatap-tunnel.netdev', '25-tunnel.network')
1781        start_networkd()
1782        self.wait_online(['isataptun99:routable', 'dummy98:degraded'])
1783
1784        output = check_output('ip -d link show isataptun99')
1785        print(output)
1786        self.assertRegex(output, "isatap ")
1787
1788    def test_6rd_tunnel(self):
1789        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-6rd.network',
1790                                        '25-6rd-tunnel.netdev', '25-tunnel.network')
1791        start_networkd()
1792        self.wait_online(['sittun99:routable', 'dummy98:degraded'])
1793
1794        output = check_output('ip -d link show sittun99')
1795        print(output)
1796        self.assertRegex(output, '6rd-prefix 2602::/24')
1797
1798    @expectedFailureIfERSPANModuleIsNotAvailable()
1799    def test_erspan_tunnel(self):
1800        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-erspan.network',
1801                                        '25-erspan-tunnel.netdev', '25-tunnel.network',
1802                                        '25-erspan-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1803        start_networkd()
1804        self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
1805
1806        output = check_output('ip -d link show erspan99')
1807        print(output)
1808        self.assertRegex(output, 'erspan remote 172.16.1.100 local 172.16.1.200')
1809        self.assertRegex(output, 'ikey 0.0.0.101')
1810        self.assertRegex(output, 'okey 0.0.0.101')
1811        self.assertRegex(output, 'iseq')
1812        self.assertRegex(output, 'oseq')
1813        output = check_output('ip -d link show erspan98')
1814        print(output)
1815        self.assertRegex(output, 'erspan remote 172.16.1.100 local any')
1816        self.assertRegex(output, '102')
1817        self.assertRegex(output, 'ikey 0.0.0.102')
1818        self.assertRegex(output, 'okey 0.0.0.102')
1819        self.assertRegex(output, 'iseq')
1820        self.assertRegex(output, 'oseq')
1821
1822    def test_tunnel_independent(self):
1823        copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
1824        start_networkd()
1825
1826        self.wait_online(['ipiptun99:carrier'])
1827
1828    def test_tunnel_independent_loopback(self):
1829        copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
1830        start_networkd()
1831
1832        self.wait_online(['ipiptun99:carrier'])
1833
1834    @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
1835    def test_xfrm(self):
1836        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-xfrm.network',
1837                                        '25-xfrm.netdev', '25-xfrm-independent.netdev',
1838                                        '26-netdev-link-local-addressing-yes.network')
1839        start_networkd()
1840
1841        self.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
1842
1843        output = check_output('ip -d link show dev xfrm98')
1844        print(output)
1845        self.assertIn('xfrm98@dummy98:', output)
1846        self.assertIn('xfrm if_id 0x98 ', output)
1847
1848        output = check_output('ip -d link show dev xfrm99')
1849        print(output)
1850        self.assertIn('xfrm99@lo:', output)
1851        self.assertIn('xfrm if_id 0x99 ', output)
1852
1853    @expectedFailureIfModuleIsNotAvailable('fou')
1854    def test_fou(self):
1855        # The following redundant check is necessary for CentOS CI.
1856        # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
1857        self.assertTrue(is_module_available('fou'))
1858
1859        copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
1860                                        '25-fou-ipip.netdev', '25-fou-sit.netdev',
1861                                        '25-fou-gre.netdev', '25-fou-gretap.netdev')
1862        start_networkd()
1863
1864        self.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state='unmanaged')
1865
1866        output = check_output('ip fou show')
1867        print(output)
1868        self.assertRegex(output, 'port 55555 ipproto 4')
1869        self.assertRegex(output, 'port 55556 ipproto 47')
1870
1871        output = check_output('ip -d link show ipiptun96')
1872        print(output)
1873        self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
1874        output = check_output('ip -d link show sittun96')
1875        print(output)
1876        self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
1877        output = check_output('ip -d link show gretun96')
1878        print(output)
1879        self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
1880        output = check_output('ip -d link show gretap96')
1881        print(output)
1882        self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
1883
1884    def test_vxlan(self):
1885        copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-vxlan-test1.network',
1886                                        '25-vxlan.netdev', '25-vxlan.network',
1887                                        '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
1888                                        '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
1889                                        '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
1890                                        '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
1891        start_networkd()
1892
1893        self.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
1894                          'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
1895
1896        output = check_output('ip -d link show vxlan99')
1897        print(output)
1898        self.assertIn('999', output)
1899        self.assertIn('5555', output)
1900        self.assertIn('l2miss', output)
1901        self.assertIn('l3miss', output)
1902        self.assertIn('udpcsum', output)
1903        self.assertIn('udp6zerocsumtx', output)
1904        self.assertIn('udp6zerocsumrx', output)
1905        self.assertIn('remcsumtx', output)
1906        self.assertIn('remcsumrx', output)
1907        self.assertIn('gbp', output)
1908
1909        output = check_output('bridge fdb show dev vxlan99')
1910        print(output)
1911        self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
1912        self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
1913        self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
1914
1915        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env)
1916        print(output)
1917        self.assertIn('VNI: 999', output)
1918        self.assertIn('Destination Port: 5555', output)
1919        self.assertIn('Underlying Device: test1', output)
1920
1921        output = check_output('bridge fdb show dev vxlan97')
1922        print(output)
1923        self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
1924        self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
1925        self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
1926
1927        output = check_output('ip -d link show vxlan-slaac')
1928        print(output)
1929        self.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
1930
1931        output = check_output('ip -6 address show veth99')
1932        print(output)
1933        self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
1934
1935    @unittest.skip(reason="Causes kernel panic on recent kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
1936    def test_macsec(self):
1937        copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
1938                                        '26-macsec.network', '12-dummy.netdev')
1939        start_networkd()
1940
1941        self.wait_online(['dummy98:degraded', 'macsec99:routable'])
1942
1943        output = check_output('ip -d link show macsec99')
1944        print(output)
1945        self.assertRegex(output, 'macsec99@dummy98')
1946        self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
1947        self.assertRegex(output, 'encrypt on')
1948
1949        output = check_output('ip macsec show macsec99')
1950        print(output)
1951        self.assertRegex(output, 'encrypt on')
1952        self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
1953        self.assertRegex(output, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
1954        self.assertRegex(output, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
1955        self.assertRegex(output, 'RXSC: c619528fe6a00100, state on')
1956        self.assertRegex(output, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
1957        self.assertRegex(output, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
1958        self.assertRegex(output, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
1959        self.assertRegex(output, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
1960        self.assertNotRegex(output, 'key 02030405067080900000000000000000')
1961        self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
1962        self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
1963
1964    def test_nlmon(self):
1965        copy_unit_to_networkd_unit_path('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
1966        start_networkd()
1967
1968        self.wait_online(['nlmon99:carrier'])
1969
1970    @expectedFailureIfModuleIsNotAvailable('ifb')
1971    def test_ifb(self):
1972        copy_unit_to_networkd_unit_path('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
1973        start_networkd()
1974
1975        self.wait_online(['ifb99:degraded'])
1976
1977class NetworkdL2TPTests(unittest.TestCase, Utilities):
1978
1979    links =[
1980        'l2tp-ses1',
1981        'l2tp-ses2',
1982        'l2tp-ses3',
1983        'l2tp-ses4',
1984        'test1']
1985
1986    units = [
1987        '11-dummy.netdev',
1988        '25-l2tp-dummy.network',
1989        '25-l2tp.network',
1990        '25-l2tp-ip.netdev',
1991        '25-l2tp-udp.netdev']
1992
1993    l2tp_tunnel_ids = [ '10' ]
1994
1995    def setUp(self):
1996        remove_l2tp_tunnels(self.l2tp_tunnel_ids)
1997        remove_links(self.links)
1998        stop_networkd(show_logs=False)
1999
2000    def tearDown(self):
2001        remove_l2tp_tunnels(self.l2tp_tunnel_ids)
2002        remove_links(self.links)
2003        remove_unit_from_networkd_path(self.units)
2004        stop_networkd(show_logs=True)
2005
2006    @expectedFailureIfModuleIsNotAvailable('l2tp_eth')
2007    def test_l2tp_udp(self):
2008        copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network',
2009                                        '25-l2tp-udp.netdev', '25-l2tp.network')
2010        start_networkd()
2011
2012        self.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
2013
2014        output = check_output('ip l2tp show tunnel tunnel_id 10')
2015        print(output)
2016        self.assertRegex(output, "Tunnel 10, encap UDP")
2017        self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
2018        self.assertRegex(output, "Peer tunnel 11")
2019        self.assertRegex(output, "UDP source / dest ports: 3000/4000")
2020        self.assertRegex(output, "UDP checksum: enabled")
2021
2022        output = check_output('ip l2tp show session tid 10 session_id 15')
2023        print(output)
2024        self.assertRegex(output, "Session 15 in tunnel 10")
2025        self.assertRegex(output, "Peer session 16, tunnel 11")
2026        self.assertRegex(output, "interface name: l2tp-ses1")
2027
2028        output = check_output('ip l2tp show session tid 10 session_id 17')
2029        print(output)
2030        self.assertRegex(output, "Session 17 in tunnel 10")
2031        self.assertRegex(output, "Peer session 18, tunnel 11")
2032        self.assertRegex(output, "interface name: l2tp-ses2")
2033
2034    @expectedFailureIfModuleIsNotAvailable('l2tp_ip')
2035    def test_l2tp_ip(self):
2036        copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network',
2037                                        '25-l2tp-ip.netdev', '25-l2tp.network')
2038        start_networkd()
2039
2040        self.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
2041
2042        output = check_output('ip l2tp show tunnel tunnel_id 10')
2043        print(output)
2044        self.assertRegex(output, "Tunnel 10, encap IP")
2045        self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
2046        self.assertRegex(output, "Peer tunnel 12")
2047
2048        output = check_output('ip l2tp show session tid 10 session_id 25')
2049        print(output)
2050        self.assertRegex(output, "Session 25 in tunnel 10")
2051        self.assertRegex(output, "Peer session 26, tunnel 12")
2052        self.assertRegex(output, "interface name: l2tp-ses3")
2053
2054        output = check_output('ip l2tp show session tid 10 session_id 27')
2055        print(output)
2056        self.assertRegex(output, "Session 27 in tunnel 10")
2057        self.assertRegex(output, "Peer session 28, tunnel 12")
2058        self.assertRegex(output, "interface name: l2tp-ses4")
2059
2060class NetworkdNetworkTests(unittest.TestCase, Utilities):
2061    links = [
2062        'bond199',
2063        'dummy98',
2064        'dummy99',
2065        'gretun97',
2066        'ip6gretun97',
2067        'test1',
2068        'veth-peer',
2069        'veth99',
2070        'vlan99',
2071        'vrf99',
2072    ]
2073
2074    units = [
2075        '11-dummy.netdev',
2076        '12-dummy.netdev',
2077        '12-dummy.network',
2078        '21-vlan.netdev',
2079        '21-vlan-test1.network',
2080        '23-active-slave.network',
2081        '24-keep-configuration-static.network',
2082        '24-search-domain.network',
2083        '25-address-ipv4acd-veth99.network',
2084        '25-address-link-section.network',
2085        '25-address-peer-ipv4.network',
2086        '25-address-static.network',
2087        '25-activation-policy.network',
2088        '25-bind-carrier.network',
2089        '25-bond-active-backup-slave.netdev',
2090        '25-fibrule-invert.network',
2091        '25-fibrule-port-range.network',
2092        '25-fibrule-uidrange.network',
2093        '25-gre-tunnel-remote-any.netdev',
2094        '25-ip6gre-tunnel-remote-any.netdev',
2095        '25-ipv6-address-label-section.network',
2096        '25-ipv6-proxy-ndp.network',
2097        '25-link-local-addressing-no.network',
2098        '25-link-local-addressing-yes.network',
2099        '25-link-section-unmanaged.network',
2100        '25-neighbor-section.network',
2101        '25-neighbor-next.network',
2102        '25-neighbor-ipv6.network',
2103        '25-neighbor-ip-dummy.network',
2104        '25-neighbor-ip.network',
2105        '25-nexthop-dummy.network',
2106        '25-nexthop-nothing.network',
2107        '25-nexthop.network',
2108        '25-qdisc-cake.network',
2109        '25-qdisc-clsact-and-htb.network',
2110        '25-qdisc-drr.network',
2111        '25-qdisc-ets.network',
2112        '25-qdisc-fq_pie.network',
2113        '25-qdisc-hhf.network',
2114        '25-qdisc-ingress-netem-compat.network',
2115        '25-qdisc-pie.network',
2116        '25-qdisc-qfq.network',
2117        '25-prefix-route-with-vrf.network',
2118        '25-prefix-route-without-vrf.network',
2119        '25-route-ipv6-src.network',
2120        '25-route-static.network',
2121        '25-route-via-ipv6.network',
2122        '25-route-vrf.network',
2123        '25-gateway-static.network',
2124        '25-gateway-next-static.network',
2125        '25-sysctl-disable-ipv6.network',
2126        '25-sysctl.network',
2127        '25-test1.network',
2128        '25-veth-peer.network',
2129        '25-veth.netdev',
2130        '25-vrf.netdev',
2131        '25-vrf.network',
2132        '26-link-local-addressing-ipv6.network',
2133        '25-dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network',
2134        '25-dhcp-server-with-ipv6-prefix.network',
2135        '25-ipv6ra-prefix-client-with-static-ipv4-address.network',
2136        '25-ipv6-prefix-with-delay.network',
2137        '25-routing-policy-rule-dummy98.network',
2138        '25-routing-policy-rule-test1.network',
2139        '25-routing-policy-rule-reconfigure1.network',
2140        '25-routing-policy-rule-reconfigure2.network',
2141    ]
2142
2143    networkd_conf_dropins = [
2144        'networkd-manage-foreign-routes-no.conf',
2145    ]
2146
2147    routing_policy_rule_tables = ['7', '8', '9', '10', '1011']
2148    routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']]
2149
2150    def setUp(self):
2151        remove_blackhole_nexthops()
2152        remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
2153        remove_routes(self.routes)
2154        remove_links(self.links)
2155        stop_networkd(show_logs=False)
2156        call('ip netns del ns99', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
2157
2158    def tearDown(self):
2159        remove_blackhole_nexthops()
2160        remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
2161        remove_routes(self.routes)
2162        remove_links(self.links)
2163        remove_unit_from_networkd_path(self.units)
2164        remove_networkd_conf_dropin(self.networkd_conf_dropins)
2165        stop_networkd(show_logs=True)
2166        call('ip netns del ns99', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
2167
2168    def test_address_static(self):
2169        # test for #22515. The address will be removed and replaced with /64 prefix.
2170        rc = call('ip link add dummy98 type dummy')
2171        self.assertEqual(rc, 0)
2172        rc = call('ip link set dev dummy98 up')
2173        self.assertEqual(rc, 0)
2174        rc = call('ip -6 address add 2001:db8:0:f101::15/128 dev dummy98')
2175        self.assertEqual(rc, 0)
2176        self.wait_address('dummy98', '2001:db8:0:f101::15/128', ipv='-6')
2177        rc = call('ip -4 address add 10.3.2.3/16 brd 10.3.255.250 scope global label dummy98:hoge dev dummy98')
2178        self.assertEqual(rc, 0)
2179        self.wait_address('dummy98', '10.3.2.3/16 brd 10.3.255.250', ipv='-4')
2180
2181        copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
2182        start_networkd()
2183
2184        self.wait_online(['dummy98:routable'])
2185
2186        output = check_output('ip -4 address show dev dummy98')
2187        print(output)
2188        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
2189        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
2190        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
2191        self.assertIn('inet 10.7.8.9/16 brd 10.7.255.255 scope link deprecated dummy98', output)
2192        self.assertIn('inet 10.8.8.1/16 scope global dummy98', output)
2193        self.assertIn('inet 10.8.8.2/16 brd 10.8.8.128 scope global secondary dummy98', output)
2194        self.assertRegex(output, 'inet 10.9.0.1/16 (metric 128 |)brd 10.9.255.255 scope global dummy98')
2195
2196        # test for ENOBUFS issue #17012
2197        for i in range(1,254):
2198            self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
2199
2200        # invalid sections
2201        self.assertNotIn('10.10.0.1/16', output)
2202        self.assertNotIn('10.10.0.2/16', output)
2203
2204        output = check_output('ip -4 address show dev dummy98 label 32')
2205        self.assertIn('inet 10.3.2.3/16 brd 10.3.255.255 scope global 32', output)
2206
2207        output = check_output('ip -4 address show dev dummy98 label 33')
2208        self.assertIn('inet 10.4.2.3 peer 10.4.2.4/16 scope global 33', output)
2209
2210        output = check_output('ip -4 address show dev dummy98 label 34')
2211        self.assertRegex(output, r'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255 scope global 34')
2212
2213        output = check_output('ip -4 address show dev dummy98 label 35')
2214        self.assertRegex(output, r'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255 scope global 35')
2215
2216        output = check_output('ip -4 route show dev dummy98')
2217        print(output)
2218        self.assertIn('10.9.0.0/16 proto kernel scope link src 10.9.0.1 metric 128', output)
2219
2220        output = check_output('ip -6 address show dev dummy98')
2221        print(output)
2222        self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
2223        self.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output)
2224        self.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output)
2225        self.assertIn('inet6 2001:db8:0:f102::16/64 scope global', output)
2226        self.assertIn('inet6 2001:db8:0:f103::20 peer 2001:db8:0:f103::10/128 scope global', output)
2227        self.assertIn('inet6 2001:db8:1:f101::1/64 scope global deprecated', output)
2228        self.assertRegex(output, r'inet6 fd[0-9a-f:]*1/64 scope global')
2229
2230        # Tests for #20891.
2231        # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2232        self.assertEqual(call('ip address change 10.7.8.9/16 dev dummy98 preferred_lft forever'), 0)
2233        self.assertEqual(call('ip address change 2001:db8:1:f101::1/64 dev dummy98 preferred_lft forever'), 0)
2234        output = check_output('ip -4 address show dev dummy98')
2235        print(output)
2236        self.assertNotIn('deprecated', output)
2237        output = check_output('ip -6 address show dev dummy98')
2238        print(output)
2239        self.assertNotIn('deprecated', output)
2240
2241        # 2. reconfigure the interface.
2242        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
2243        self.wait_online(['dummy98:routable'])
2244
2245        # 3. check the deprecated flag is set for the address configured with PreferredLifetime=0
2246        output = check_output('ip -4 address show dev dummy98')
2247        print(output)
2248        self.assertIn('inet 10.7.8.9/16 brd 10.7.255.255 scope link deprecated dummy98', output)
2249        output = check_output('ip -6 address show dev dummy98')
2250        print(output)
2251        self.assertIn('inet6 2001:db8:1:f101::1/64 scope global deprecated', output)
2252
2253        # test for ENOBUFS issue #17012
2254        output = check_output('ip -4 address show dev dummy98')
2255        for i in range(1,254):
2256            self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
2257
2258        # TODO: check json string
2259        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
2260
2261    def test_address_ipv4acd(self):
2262        check_output('ip netns add ns99')
2263        check_output('ip link add veth99 type veth peer veth-peer')
2264        check_output('ip link set veth-peer netns ns99')
2265        check_output('ip link set veth99 up')
2266        check_output('ip netns exec ns99 ip link set veth-peer up')
2267        check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2268
2269        copy_unit_to_networkd_unit_path('25-address-ipv4acd-veth99.network', dropins=False)
2270        start_networkd()
2271        self.wait_online(['veth99:routable'])
2272
2273        output = check_output('ip -4 address show dev veth99')
2274        print(output)
2275        self.assertNotIn('192.168.100.10/24', output)
2276        self.assertIn('192.168.100.11/24', output)
2277
2278        copy_unit_to_networkd_unit_path('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2279        run(*networkctl_cmd, 'reload', env=env)
2280        time.sleep(1)
2281        rc = call(*wait_online_cmd, '--timeout=10s', '--interface=veth99:routable', env=env)
2282        self.assertTrue(rc == 1)
2283
2284        output = check_output('ip -4 address show dev veth99')
2285        print(output)
2286        self.assertNotIn('192.168.100.10/24', output)
2287        self.assertIn('192.168.100.11/24', output)
2288
2289    def test_address_peer_ipv4(self):
2290        # test for issue #17304
2291        copy_unit_to_networkd_unit_path('25-address-peer-ipv4.network', '12-dummy.netdev')
2292
2293        for trial in range(2):
2294            if trial == 0:
2295                start_networkd()
2296            else:
2297                restart_networkd()
2298
2299            self.wait_online(['dummy98:routable'])
2300
2301            output = check_output('ip -4 address show dev dummy98')
2302            self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
2303
2304    @expectedFailureIfModuleIsNotAvailable('vrf')
2305    def test_prefix_route(self):
2306        copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2307                                        '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2308                                        '25-vrf.netdev', '25-vrf.network')
2309        for trial in range(2):
2310            if trial == 0:
2311                start_networkd()
2312            else:
2313                restart_networkd(3)
2314
2315            self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
2316
2317            output = check_output('ip route show table 42 dev dummy98')
2318            print('### ip route show table 42 dev dummy98')
2319            print(output)
2320            self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2321            self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2322            self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2323            self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2324            self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2325            self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2326            self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2327            output = check_output('ip -6 route show table 42 dev dummy98')
2328            print('### ip -6 route show table 42 dev dummy98')
2329            print(output)
2330            if trial == 0:
2331                # Kernel's bug?
2332                self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2333            #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2334            self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2335            self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2336            self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2337            self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2338            self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
2339            self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2340
2341            print()
2342
2343            output = check_output('ip route show dev test1')
2344            print('### ip route show dev test1')
2345            print(output)
2346            self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2347            output = check_output('ip route show table local dev test1')
2348            print('### ip route show table local dev test1')
2349            print(output)
2350            self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2351            self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2352            self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2353            self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2354            self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2355            self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2356            output = check_output('ip -6 route show dev test1')
2357            print('### ip -6 route show dev test1')
2358            print(output)
2359            self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2360            self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2361            self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
2362            output = check_output('ip -6 route show table local dev test1')
2363            print('### ip -6 route show table local dev test1')
2364            print(output)
2365            self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2366            self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2367            self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2368            self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2369            self.assertRegex(output, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2370
2371    def test_configure_without_carrier(self):
2372        copy_unit_to_networkd_unit_path('11-dummy.netdev')
2373        start_networkd()
2374        self.wait_operstate('test1', 'off', '')
2375        check_output('ip link set dev test1 up carrier off')
2376
2377        copy_unit_to_networkd_unit_path('25-test1.network.d/configure-without-carrier.conf', dropins=False)
2378        restart_networkd()
2379        self.wait_online(['test1:no-carrier'])
2380
2381        carrier_map = {'on': '1', 'off': '0'}
2382        routable_map = {'on': 'routable', 'off': 'no-carrier'}
2383        for carrier in ['off', 'on', 'off']:
2384            with self.subTest(carrier=carrier):
2385                if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
2386                    check_output(f'ip link set dev test1 carrier {carrier}')
2387                self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2388
2389                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
2390                print(output)
2391                self.assertRegex(output, '192.168.0.15')
2392                self.assertRegex(output, '192.168.0.1')
2393                self.assertRegex(output, routable_map[carrier])
2394
2395    def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
2396        copy_unit_to_networkd_unit_path('11-dummy.netdev')
2397        start_networkd()
2398        self.wait_operstate('test1', 'off', '')
2399        check_output('ip link set dev test1 up carrier off')
2400
2401        copy_unit_to_networkd_unit_path('25-test1.network')
2402        restart_networkd()
2403        self.wait_online(['test1:no-carrier'])
2404
2405        carrier_map = {'on': '1', 'off': '0'}
2406        routable_map = {'on': 'routable', 'off': 'no-carrier'}
2407        for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
2408            with self.subTest(carrier=carrier, have_config=have_config):
2409                if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
2410                    check_output(f'ip link set dev test1 carrier {carrier}')
2411                self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
2412
2413                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
2414                print(output)
2415                if have_config:
2416                    self.assertRegex(output, '192.168.0.15')
2417                    self.assertRegex(output, '192.168.0.1')
2418                else:
2419                    self.assertNotRegex(output, '192.168.0.15')
2420                    self.assertNotRegex(output, '192.168.0.1')
2421                self.assertRegex(output, routable_map[carrier])
2422
2423    def test_routing_policy_rule(self):
2424        copy_unit_to_networkd_unit_path('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2425        start_networkd()
2426        self.wait_online(['test1:degraded'])
2427
2428        output = check_output('ip rule list iif test1 priority 111')
2429        print(output)
2430        self.assertRegex(output, '111:')
2431        self.assertRegex(output, 'from 192.168.100.18')
2432        self.assertRegex(output, r'tos (0x08|throughput)\s')
2433        self.assertRegex(output, 'iif test1')
2434        self.assertRegex(output, 'oif test1')
2435        self.assertRegex(output, 'lookup 7')
2436
2437        output = check_output('ip rule list iif test1 priority 101')
2438        print(output)
2439        self.assertRegex(output, '101:')
2440        self.assertRegex(output, 'from all')
2441        self.assertRegex(output, 'iif test1')
2442        self.assertRegex(output, 'lookup 9')
2443
2444        output = check_output('ip -6 rule list iif test1 priority 100')
2445        print(output)
2446        self.assertRegex(output, '100:')
2447        self.assertRegex(output, 'from all')
2448        self.assertRegex(output, 'iif test1')
2449        self.assertRegex(output, 'lookup 8')
2450
2451        output = check_output('ip rule list iif test1 priority 102')
2452        print(output)
2453        self.assertRegex(output, '102:')
2454        self.assertRegex(output, 'from 0.0.0.0/8')
2455        self.assertRegex(output, 'iif test1')
2456        self.assertRegex(output, 'lookup 10')
2457
2458        # TODO: check json string
2459        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
2460
2461    def test_routing_policy_rule_issue_11280(self):
2462        copy_unit_to_networkd_unit_path('25-routing-policy-rule-test1.network', '11-dummy.netdev',
2463                                        '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
2464
2465        for _ in range(3):
2466            # Remove state files only first time
2467            start_networkd(3)
2468            self.wait_online(['test1:degraded', 'dummy98:degraded'])
2469            time.sleep(1)
2470
2471            output = check_output('ip rule list table 7')
2472            print(output)
2473            self.assertRegex(output, '111:	from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2474
2475            output = check_output('ip rule list table 8')
2476            print(output)
2477            self.assertRegex(output, '112:	from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2478
2479            stop_networkd(remove_state_files=False)
2480
2481    def test_routing_policy_rule_reconfigure(self):
2482        copy_unit_to_networkd_unit_path('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
2483        start_networkd()
2484        self.wait_online(['test1:degraded'])
2485
2486        output = check_output('ip rule list table 1011')
2487        print(output)
2488        self.assertIn('10111:	from all fwmark 0x3f3 lookup 1011', output)
2489        self.assertIn('10112:	from all oif test1 lookup 1011', output)
2490        self.assertIn('10113:	from all iif test1 lookup 1011', output)
2491        self.assertIn('10114:	from 192.168.8.254 lookup 1011', output)
2492
2493        output = check_output('ip -6 rule list table 1011')
2494        print(output)
2495        self.assertIn('10112:	from all oif test1 lookup 1011', output)
2496
2497        copy_unit_to_networkd_unit_path('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
2498        run(*networkctl_cmd, 'reload', env=env)
2499        time.sleep(1)
2500        self.wait_online(['test1:degraded'])
2501
2502        output = check_output('ip rule list table 1011')
2503        print(output)
2504        self.assertIn('10111:	from all fwmark 0x3f3 lookup 1011', output)
2505        self.assertIn('10112:	from all oif test1 lookup 1011', output)
2506        self.assertIn('10113:	from all iif test1 lookup 1011', output)
2507        self.assertIn('10114:	from 192.168.8.254 lookup 1011', output)
2508
2509        output = check_output('ip -6 rule list table 1011')
2510        print(output)
2511        self.assertNotIn('10112:	from all oif test1 lookup 1011', output)
2512        self.assertIn('10113:	from all iif test1 lookup 1011', output)
2513
2514        run('ip rule delete priority 10111')
2515        run('ip rule delete priority 10112')
2516        run('ip rule delete priority 10113')
2517        run('ip rule delete priority 10114')
2518        run('ip -6 rule delete priority 10113')
2519
2520        output = check_output('ip rule list table 1011')
2521        print(output)
2522        self.assertEqual(output, '')
2523
2524        output = check_output('ip -6 rule list table 1011')
2525        print(output)
2526        self.assertEqual(output, '')
2527
2528        run(*networkctl_cmd, 'reconfigure', 'test1', env=env)
2529        self.wait_online(['test1:degraded'])
2530
2531        output = check_output('ip rule list table 1011')
2532        print(output)
2533        self.assertIn('10111:	from all fwmark 0x3f3 lookup 1011', output)
2534        self.assertIn('10112:	from all oif test1 lookup 1011', output)
2535        self.assertIn('10113:	from all iif test1 lookup 1011', output)
2536        self.assertIn('10114:	from 192.168.8.254 lookup 1011', output)
2537
2538        output = check_output('ip -6 rule list table 1011')
2539        print(output)
2540        self.assertIn('10113:	from all iif test1 lookup 1011', output)
2541
2542    @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
2543    def test_routing_policy_rule_port_range(self):
2544        copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
2545        start_networkd()
2546        self.wait_online(['test1:degraded'])
2547
2548        output = check_output('ip rule')
2549        print(output)
2550        self.assertRegex(output, '111')
2551        self.assertRegex(output, 'from 192.168.100.18')
2552        self.assertRegex(output, '1123-1150')
2553        self.assertRegex(output, '3224-3290')
2554        self.assertRegex(output, 'tcp')
2555        self.assertRegex(output, 'lookup 7')
2556
2557    @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
2558    def test_routing_policy_rule_invert(self):
2559        copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev')
2560        start_networkd()
2561        self.wait_online(['test1:degraded'])
2562
2563        output = check_output('ip rule')
2564        print(output)
2565        self.assertRegex(output, '111')
2566        self.assertRegex(output, 'not.*?from.*?192.168.100.18')
2567        self.assertRegex(output, 'tcp')
2568        self.assertRegex(output, 'lookup 7')
2569
2570    @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
2571    def test_routing_policy_rule_uidrange(self):
2572        copy_unit_to_networkd_unit_path('25-fibrule-uidrange.network', '11-dummy.netdev')
2573        start_networkd()
2574        self.wait_online(['test1:degraded'])
2575
2576        output = check_output('ip rule')
2577        print(output)
2578        self.assertRegex(output, '111')
2579        self.assertRegex(output, 'from 192.168.100.18')
2580        self.assertRegex(output, 'lookup 7')
2581        self.assertRegex(output, 'uidrange 100-200')
2582
2583    def _test_route_static(self, manage_foreign_routes):
2584        if not manage_foreign_routes:
2585            copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
2586
2587        copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
2588        start_networkd()
2589        self.wait_online(['dummy98:routable'])
2590
2591        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
2592        print(output)
2593
2594        print('### ip -6 route show dev dummy98')
2595        output = check_output('ip -6 route show dev dummy98')
2596        print(output)
2597        self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
2598        self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
2599        self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
2600
2601        print('### ip -6 route show default')
2602        output = check_output('ip -6 route show default')
2603        print(output)
2604        self.assertIn('default', output)
2605        self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output)
2606
2607        print('### ip -4 route show dev dummy98')
2608        output = check_output('ip -4 route show dev dummy98')
2609        print(output)
2610        self.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output)
2611        self.assertIn('149.10.124.64 proto static scope link', output)
2612        self.assertIn('169.254.0.0/16 proto static scope link metric 2048', output)
2613        self.assertIn('192.168.1.1 proto static scope link initcwnd 20', output)
2614        self.assertIn('192.168.1.2 proto static scope link initrwnd 30', output)
2615        self.assertIn('192.168.1.3 proto static scope link advmss 30', output)
2616        self.assertIn('multicast 149.10.123.4 proto static', output)
2617
2618        print('### ip -4 route show dev dummy98 default')
2619        output = check_output('ip -4 route show dev dummy98 default')
2620        print(output)
2621        self.assertIn('default via 149.10.125.65 proto static onlink', output)
2622        self.assertIn('default via 149.10.124.64 proto static', output)
2623        self.assertIn('default proto static', output)
2624
2625        print('### ip -4 route show table local dev dummy98')
2626        output = check_output('ip -4 route show table local dev dummy98')
2627        print(output)
2628        self.assertIn('local 149.10.123.1 proto static scope host', output)
2629        self.assertIn('anycast 149.10.123.2 proto static scope link', output)
2630        self.assertIn('broadcast 149.10.123.3 proto static scope link', output)
2631
2632        print('### ip route show type blackhole')
2633        output = check_output('ip route show type blackhole')
2634        print(output)
2635        self.assertIn('blackhole 202.54.1.2 proto static', output)
2636
2637        print('### ip route show type unreachable')
2638        output = check_output('ip route show type unreachable')
2639        print(output)
2640        self.assertIn('unreachable 202.54.1.3 proto static', output)
2641
2642        print('### ip route show type prohibit')
2643        output = check_output('ip route show type prohibit')
2644        print(output)
2645        self.assertIn('prohibit 202.54.1.4 proto static', output)
2646
2647        print('### ip -6 route show type blackhole')
2648        output = check_output('ip -6 route show type blackhole')
2649        print(output)
2650        self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
2651
2652        print('### ip -6 route show type unreachable')
2653        output = check_output('ip -6 route show type unreachable')
2654        print(output)
2655        self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
2656
2657        print('### ip -6 route show type prohibit')
2658        output = check_output('ip -6 route show type prohibit')
2659        print(output)
2660        self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
2661
2662        print('### ip route show 192.168.10.1')
2663        output = check_output('ip route show 192.168.10.1')
2664        print(output)
2665        self.assertIn('192.168.10.1 proto static', output)
2666        self.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output)
2667        self.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output)
2668
2669        print('### ip route show 192.168.10.2')
2670        output = check_output('ip route show 192.168.10.2')
2671        print(output)
2672        # old ip command does not show IPv6 gateways...
2673        self.assertIn('192.168.10.2 proto static', output)
2674        self.assertIn('nexthop', output)
2675        self.assertIn('dev dummy98 weight 10', output)
2676        self.assertIn('dev dummy98 weight 5', output)
2677
2678        print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
2679        output = check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
2680        print(output)
2681        # old ip command does not show 'nexthop' keyword and weight...
2682        self.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output)
2683        self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
2684        self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output)
2685
2686        # TODO: check json string
2687        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
2688
2689        copy_unit_to_networkd_unit_path('25-address-static.network')
2690        check_output(*networkctl_cmd, 'reload', env=env)
2691        time.sleep(1)
2692        self.wait_online(['dummy98:routable'])
2693
2694        # check all routes managed by Manager are removed
2695        print('### ip route show type blackhole')
2696        output = check_output('ip route show type blackhole')
2697        print(output)
2698        self.assertEqual(output, '')
2699
2700        print('### ip route show type unreachable')
2701        output = check_output('ip route show type unreachable')
2702        print(output)
2703        self.assertEqual(output, '')
2704
2705        print('### ip route show type prohibit')
2706        output = check_output('ip route show type prohibit')
2707        print(output)
2708        self.assertEqual(output, '')
2709
2710        print('### ip -6 route show type blackhole')
2711        output = check_output('ip -6 route show type blackhole')
2712        print(output)
2713        self.assertEqual(output, '')
2714
2715        print('### ip -6 route show type unreachable')
2716        output = check_output('ip -6 route show type unreachable')
2717        print(output)
2718        self.assertEqual(output, '')
2719
2720        print('### ip -6 route show type prohibit')
2721        output = check_output('ip -6 route show type prohibit')
2722        print(output)
2723        self.assertEqual(output, '')
2724
2725        remove_unit_from_networkd_path(['25-address-static.network'])
2726        check_output(*networkctl_cmd, 'reload', env=env)
2727        time.sleep(1)
2728        self.wait_online(['dummy98:routable'])
2729
2730        # check all routes managed by Manager are reconfigured
2731        print('### ip route show type blackhole')
2732        output = check_output('ip route show type blackhole')
2733        print(output)
2734        self.assertIn('blackhole 202.54.1.2 proto static', output)
2735
2736        print('### ip route show type unreachable')
2737        output = check_output('ip route show type unreachable')
2738        print(output)
2739        self.assertIn('unreachable 202.54.1.3 proto static', output)
2740
2741        print('### ip route show type prohibit')
2742        output = check_output('ip route show type prohibit')
2743        print(output)
2744        self.assertIn('prohibit 202.54.1.4 proto static', output)
2745
2746        print('### ip -6 route show type blackhole')
2747        output = check_output('ip -6 route show type blackhole')
2748        print(output)
2749        self.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output)
2750
2751        print('### ip -6 route show type unreachable')
2752        output = check_output('ip -6 route show type unreachable')
2753        print(output)
2754        self.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output)
2755
2756        print('### ip -6 route show type prohibit')
2757        output = check_output('ip -6 route show type prohibit')
2758        print(output)
2759        self.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output)
2760
2761        rc = call("ip link del dummy98")
2762        self.assertEqual(rc, 0)
2763        time.sleep(2)
2764
2765        # check all routes managed by Manager are removed
2766        print('### ip route show type blackhole')
2767        output = check_output('ip route show type blackhole')
2768        print(output)
2769        self.assertEqual(output, '')
2770
2771        print('### ip route show type unreachable')
2772        output = check_output('ip route show type unreachable')
2773        print(output)
2774        self.assertEqual(output, '')
2775
2776        print('### ip route show type prohibit')
2777        output = check_output('ip route show type prohibit')
2778        print(output)
2779        self.assertEqual(output, '')
2780
2781        print('### ip -6 route show type blackhole')
2782        output = check_output('ip -6 route show type blackhole')
2783        print(output)
2784        self.assertEqual(output, '')
2785
2786        print('### ip -6 route show type unreachable')
2787        output = check_output('ip -6 route show type unreachable')
2788        print(output)
2789        self.assertEqual(output, '')
2790
2791        print('### ip -6 route show type prohibit')
2792        output = check_output('ip -6 route show type prohibit')
2793        print(output)
2794        self.assertEqual(output, '')
2795
2796        self.tearDown()
2797
2798    def test_route_static(self):
2799        for manage_foreign_routes in [True, False]:
2800            with self.subTest(manage_foreign_routes=manage_foreign_routes):
2801                self._test_route_static(manage_foreign_routes)
2802
2803    @expectedFailureIfRTA_VIAIsNotSupported()
2804    def test_route_via_ipv6(self):
2805        copy_unit_to_networkd_unit_path('25-route-via-ipv6.network', '12-dummy.netdev')
2806        start_networkd()
2807        self.wait_online(['dummy98:routable'])
2808
2809        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
2810        print(output)
2811
2812        print('### ip -6 route show dev dummy98')
2813        output = check_output('ip -6 route show dev dummy98')
2814        print(output)
2815        self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
2816        self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
2817
2818        print('### ip -4 route show dev dummy98')
2819        output = check_output('ip -4 route show dev dummy98')
2820        print(output)
2821        self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
2822        self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
2823
2824    @expectedFailureIfModuleIsNotAvailable('vrf')
2825    def test_route_vrf(self):
2826        copy_unit_to_networkd_unit_path('25-route-vrf.network', '12-dummy.netdev',
2827                                        '25-vrf.netdev', '25-vrf.network')
2828        start_networkd()
2829        self.wait_online(['dummy98:routable', 'vrf99:carrier'])
2830
2831        output = check_output('ip route show vrf vrf99')
2832        print(output)
2833        self.assertRegex(output, 'default via 192.168.100.1')
2834
2835        output = check_output('ip route show')
2836        print(output)
2837        self.assertNotRegex(output, 'default via 192.168.100.1')
2838
2839    def test_gateway_reconfigure(self):
2840        copy_unit_to_networkd_unit_path('25-gateway-static.network', '12-dummy.netdev')
2841        start_networkd()
2842        self.wait_online(['dummy98:routable'])
2843        print('### ip -4 route show dev dummy98 default')
2844        output = check_output('ip -4 route show dev dummy98 default')
2845        print(output)
2846        self.assertRegex(output, 'default via 149.10.124.59 proto static')
2847        self.assertNotRegex(output, '149.10.124.60')
2848
2849        remove_unit_from_networkd_path(['25-gateway-static.network'])
2850        copy_unit_to_networkd_unit_path('25-gateway-next-static.network')
2851        restart_networkd(3)
2852        self.wait_online(['dummy98:routable'])
2853        print('### ip -4 route show dev dummy98 default')
2854        output = check_output('ip -4 route show dev dummy98 default')
2855        print(output)
2856        self.assertNotRegex(output, '149.10.124.59')
2857        self.assertRegex(output, 'default via 149.10.124.60 proto static')
2858
2859    def test_ip_route_ipv6_src_route(self):
2860        # a dummy device does not make the addresses go through tentative state, so we
2861        # reuse a bond from an earlier test, which does make the addresses go through
2862        # tentative state, and do our test on that
2863        copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2864        start_networkd()
2865        self.wait_online(['dummy98:enslaved', 'bond199:routable'])
2866
2867        output = check_output('ip -6 route list dev bond199')
2868        print(output)
2869        self.assertRegex(output, 'abcd::/16')
2870        self.assertRegex(output, 'src')
2871        self.assertRegex(output, '2001:1234:56:8f63::2')
2872
2873    def test_ip_link_mac_address(self):
2874        copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
2875        start_networkd()
2876        self.wait_online(['dummy98:degraded'])
2877
2878        output = check_output('ip link show dummy98')
2879        print(output)
2880        self.assertRegex(output, '00:01:02:aa:bb:cc')
2881
2882    def test_ip_link_unmanaged(self):
2883        copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
2884        start_networkd(5)
2885
2886        self.check_link_exists('dummy98')
2887
2888        self.wait_operstate('dummy98', 'off', setup_state='unmanaged')
2889
2890    def test_ipv6_address_label(self):
2891        copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
2892        start_networkd()
2893        self.wait_online(['dummy98:degraded'])
2894
2895        output = check_output('ip addrlabel list')
2896        print(output)
2897        self.assertRegex(output, '2004:da8:1::/64')
2898
2899    def test_ipv6_proxy_ndp(self):
2900        copy_unit_to_networkd_unit_path('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
2901        start_networkd()
2902
2903        self.wait_online(['dummy98:routable'])
2904
2905        output = check_output('ip neighbor show proxy dev dummy98')
2906        print(output)
2907        for i in range(1,5):
2908            self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
2909
2910    def test_neighbor_section(self):
2911        copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
2912        start_networkd()
2913        self.wait_online(['dummy98:degraded'], timeout='40s')
2914
2915        print('### ip neigh list dev dummy98')
2916        output = check_output('ip neigh list dev dummy98')
2917        print(output)
2918        self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2919        self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
2920
2921        # TODO: check json string
2922        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
2923
2924    def test_neighbor_reconfigure(self):
2925        copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
2926        start_networkd()
2927        self.wait_online(['dummy98:degraded'], timeout='40s')
2928
2929        print('### ip neigh list dev dummy98')
2930        output = check_output('ip neigh list dev dummy98')
2931        print(output)
2932        self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2933        self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
2934
2935        remove_unit_from_networkd_path(['25-neighbor-section.network'])
2936        copy_unit_to_networkd_unit_path('25-neighbor-next.network')
2937        restart_networkd(3)
2938        self.wait_online(['dummy98:degraded'], timeout='40s')
2939        print('### ip neigh list dev dummy98')
2940        output = check_output('ip neigh list dev dummy98')
2941        print(output)
2942        self.assertNotRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2943        self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:66.*PERMANENT')
2944        self.assertNotRegex(output, '2004:da8:1::1.*PERMANENT')
2945
2946    def test_neighbor_gre(self):
2947        copy_unit_to_networkd_unit_path('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
2948                                        '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
2949        start_networkd()
2950        self.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout='40s')
2951
2952        output = check_output('ip neigh list dev gretun97')
2953        print(output)
2954        self.assertRegex(output, '10.0.0.22 lladdr 10.65.223.239 PERMANENT')
2955
2956        output = check_output('ip neigh list dev ip6gretun97')
2957        print(output)
2958        self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
2959
2960        # TODO: check json string
2961        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
2962
2963    def test_link_local_addressing(self):
2964        copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev',
2965                                        '25-link-local-addressing-no.network', '12-dummy.netdev')
2966        start_networkd()
2967        self.wait_online(['test1:degraded', 'dummy98:carrier'])
2968
2969        output = check_output('ip address show dev test1')
2970        print(output)
2971        self.assertRegex(output, 'inet .* scope link')
2972        self.assertRegex(output, 'inet6 .* scope link')
2973
2974        output = check_output('ip address show dev dummy98')
2975        print(output)
2976        self.assertNotRegex(output, 'inet6* .* scope link')
2977
2978        # Documentation/networking/ip-sysctl.txt
2979        #
2980        # addr_gen_mode - INTEGER
2981        # Defines how link-local and autoconf addresses are generated.
2982        #
2983        # 0: generate address based on EUI64 (default)
2984        # 1: do no generate a link-local address, use EUI64 for addresses generated
2985        #    from autoconf
2986        # 2: generate stable privacy addresses, using the secret from
2987        #    stable_secret (RFC7217)
2988        # 3: generate stable privacy addresses, using a random secret if unset
2989
2990        self.assertEqual(read_ipv6_sysctl_attr('test1', 'stable_secret'), '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
2991        self.assertEqual(read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), '2')
2992        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1')
2993
2994    def test_link_local_addressing_ipv6ll(self):
2995        copy_unit_to_networkd_unit_path('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
2996        start_networkd()
2997        self.wait_online(['dummy98:degraded'])
2998
2999        # An IPv6LL address exists by default.
3000        output = check_output('ip address show dev dummy98')
3001        print(output)
3002        self.assertRegex(output, 'inet6 .* scope link')
3003
3004        copy_unit_to_networkd_unit_path('25-link-local-addressing-no.network')
3005        check_output(*networkctl_cmd, 'reload', env=env)
3006        time.sleep(1)
3007        self.wait_online(['dummy98:carrier'])
3008
3009        # Check if the IPv6LL address is removed.
3010        output = check_output('ip address show dev dummy98')
3011        print(output)
3012        self.assertNotRegex(output, 'inet6 .* scope link')
3013
3014        remove_unit_from_networkd_path(['25-link-local-addressing-no.network'])
3015        check_output(*networkctl_cmd, 'reload', env=env)
3016        time.sleep(1)
3017        self.wait_online(['dummy98:degraded'])
3018
3019        # Check if a new IPv6LL address is assigned.
3020        output = check_output('ip address show dev dummy98')
3021        print(output)
3022        self.assertRegex(output, 'inet6 .* scope link')
3023
3024    def test_sysctl(self):
3025        copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')
3026        start_networkd()
3027        self.wait_online(['dummy98:degraded'])
3028
3029        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
3030        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
3031        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
3032        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'hop_limit'), '5')
3033        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
3034        self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
3035        self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
3036        self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'accept_local'), '1')
3037
3038    def test_sysctl_disable_ipv6(self):
3039        copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3040
3041        print('## Disable ipv6')
3042        check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3043        check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3044
3045        start_networkd()
3046        self.wait_online(['dummy98:routable'])
3047
3048        output = check_output('ip -4 address show dummy98')
3049        print(output)
3050        self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3051        output = check_output('ip -6 address show dummy98')
3052        print(output)
3053        self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
3054        self.assertRegex(output, 'inet6 .* scope link')
3055        output = check_output('ip -4 route show dev dummy98')
3056        print(output)
3057        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3058        output = check_output('ip -6 route show default')
3059        print(output)
3060        self.assertRegex(output, 'default')
3061        self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3062
3063        check_output('ip link del dummy98')
3064
3065        print('## Enable ipv6')
3066        check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3067        check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3068
3069        restart_networkd(3)
3070        self.wait_online(['dummy98:routable'])
3071
3072        output = check_output('ip -4 address show dummy98')
3073        print(output)
3074        self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3075        output = check_output('ip -6 address show dummy98')
3076        print(output)
3077        self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
3078        self.assertRegex(output, 'inet6 .* scope link')
3079        output = check_output('ip -4 route show dev dummy98')
3080        print(output)
3081        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3082        output = check_output('ip -6 route show default')
3083        print(output)
3084        self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3085
3086    def test_bind_carrier(self):
3087        check_output('ip link add dummy98 type dummy')
3088        check_output('ip link set dummy98 up')
3089        time.sleep(2)
3090
3091        copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
3092        start_networkd()
3093        self.wait_online(['test1:routable'])
3094
3095        output = check_output('ip address show test1')
3096        print(output)
3097        self.assertRegex(output, 'UP,LOWER_UP')
3098        self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
3099        self.wait_operstate('test1', 'routable')
3100
3101        check_output('ip link add dummy99 type dummy')
3102        check_output('ip link set dummy99 up')
3103        time.sleep(2)
3104        output = check_output('ip address show test1')
3105        print(output)
3106        self.assertRegex(output, 'UP,LOWER_UP')
3107        self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
3108        self.wait_operstate('test1', 'routable')
3109
3110        check_output('ip link del dummy98')
3111        time.sleep(2)
3112        output = check_output('ip address show test1')
3113        print(output)
3114        self.assertRegex(output, 'UP,LOWER_UP')
3115        self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
3116        self.wait_operstate('test1', 'routable')
3117
3118        check_output('ip link set dummy99 down')
3119        time.sleep(2)
3120        output = check_output('ip address show test1')
3121        print(output)
3122        self.assertNotRegex(output, 'UP,LOWER_UP')
3123        self.assertRegex(output, 'DOWN')
3124        self.assertNotRegex(output, '192.168.10')
3125        self.wait_operstate('test1', 'off')
3126
3127        check_output('ip link set dummy99 up')
3128        time.sleep(2)
3129        output = check_output('ip address show test1')
3130        print(output)
3131        self.assertRegex(output, 'UP,LOWER_UP')
3132        self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
3133        self.wait_operstate('test1', 'routable')
3134
3135    def _test_activation_policy(self, test, interface):
3136        conffile = '25-activation-policy.network'
3137        if test:
3138            conffile = f'{conffile}.d/{test}.conf'
3139        if interface == 'vlan99':
3140            copy_unit_to_networkd_unit_path('21-vlan.netdev', '21-vlan-test1.network')
3141        copy_unit_to_networkd_unit_path('11-dummy.netdev', conffile, dropins=False)
3142        start_networkd()
3143
3144        always = test.startswith('always')
3145        initial_up = test != 'manual' and not test.endswith('down') # note: default is up
3146        expect_up = initial_up
3147        next_up = not expect_up
3148
3149        if test.endswith('down'):
3150            self.wait_activated(interface)
3151
3152        for iteration in range(4):
3153            with self.subTest(iteration=iteration, expect_up=expect_up):
3154                operstate = 'routable' if expect_up else 'off'
3155                setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None)
3156                self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20)
3157
3158                if expect_up:
3159                    self.assertIn('UP', check_output(f'ip link show {interface}'))
3160                    self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}'))
3161                    self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}'))
3162                else:
3163                    self.assertIn('DOWN', check_output(f'ip link show {interface}'))
3164
3165            if next_up:
3166                check_output(f'ip link set dev {interface} up')
3167            else:
3168                check_output(f'ip link set dev {interface} down')
3169            expect_up = initial_up if always else next_up
3170            next_up = not next_up
3171            if always:
3172                time.sleep(1)
3173
3174    def test_activation_policy(self):
3175        for interface in ['test1', 'vlan99']:
3176            with self.subTest(interface=interface):
3177                for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3178                    with self.subTest(test=test):
3179                        self.setUp()
3180                        self._test_activation_policy(test, interface)
3181                        self.tearDown()
3182
3183    def _test_activation_policy_required_for_online(self, policy, required):
3184        conffile = '25-activation-policy.network'
3185        units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
3186        if policy:
3187            units += [f'{conffile}.d/{policy}.conf']
3188        if required:
3189            units += [f'{conffile}.d/required-{required}.conf']
3190        copy_unit_to_networkd_unit_path(*units, dropins=False)
3191        start_networkd()
3192
3193        if policy.endswith('down'):
3194            self.wait_activated('test1')
3195
3196        if policy.endswith('down') or policy == 'manual':
3197            self.wait_operstate('test1', 'off', setup_state='configuring')
3198        else:
3199            self.wait_online(['test1'])
3200
3201        if policy == 'always-down':
3202            # if always-down, required for online is forced to no
3203            expected = False
3204        elif required:
3205            # otherwise if required for online is specified, it should match that
3206            expected = required == 'yes'
3207        elif policy:
3208            # otherwise if only policy specified, required for online defaults to
3209            # true if policy is up, always-up, or bound
3210            expected = policy.endswith('up') or policy == 'bound'
3211        else:
3212            # default is true, if neither are specified
3213            expected = True
3214
3215        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
3216        print(output)
3217
3218        yesno = 'yes' if expected else 'no'
3219        self.assertRegex(output, f'Required For Online: {yesno}')
3220
3221    def test_activation_policy_required_for_online(self):
3222        for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3223            for required in ['yes', 'no', '']:
3224                with self.subTest(policy=policy, required=required):
3225                    self.setUp()
3226                    self._test_activation_policy_required_for_online(policy, required)
3227                    self.tearDown()
3228
3229    def test_domain(self):
3230        copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
3231        start_networkd()
3232        self.wait_online(['dummy98:routable'])
3233
3234        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
3235        print(output)
3236        self.assertRegex(output, 'Address: 192.168.42.100')
3237        self.assertRegex(output, 'DNS: 192.168.42.1')
3238        self.assertRegex(output, 'Search Domains: one')
3239
3240    def test_keep_configuration_static(self):
3241        check_output('systemctl stop systemd-networkd.socket')
3242        check_output('systemctl stop systemd-networkd.service')
3243
3244        check_output('ip link add name dummy98 type dummy')
3245        check_output('ip address add 10.1.2.3/16 dev dummy98')
3246        check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3247        output = check_output('ip address show dummy98')
3248        print(output)
3249        self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
3250        self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3251        output = check_output('ip route show dev dummy98')
3252        print(output)
3253
3254        copy_unit_to_networkd_unit_path('24-keep-configuration-static.network')
3255        start_networkd()
3256        self.wait_online(['dummy98:routable'])
3257
3258        output = check_output('ip address show dummy98')
3259        print(output)
3260        self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
3261        self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3262
3263    @expectedFailureIfNexthopIsNotAvailable()
3264    def test_nexthop(self):
3265        def check_nexthop(self):
3266            self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
3267
3268            output = check_output('ip nexthop list dev veth99')
3269            print(output)
3270            self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
3271            self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
3272            self.assertIn('id 3 dev veth99', output)
3273            self.assertIn('id 4 dev veth99', output)
3274            self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3275            self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
3276            self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
3277
3278            output = check_output('ip nexthop list dev dummy98')
3279            print(output)
3280            self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
3281
3282            # kernel manages blackhole nexthops on lo
3283            output = check_output('ip nexthop list dev lo')
3284            print(output)
3285            self.assertIn('id 6 blackhole', output)
3286            self.assertIn('id 7 blackhole', output)
3287
3288            # group nexthops are shown with -0 option
3289            output = check_output('ip -0 nexthop list id 21')
3290            print(output)
3291            self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
3292
3293            output = check_output('ip route show dev veth99 10.10.10.10')
3294            print(output)
3295            self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
3296
3297            output = check_output('ip route show dev veth99 10.10.10.11')
3298            print(output)
3299            self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
3300
3301            output = check_output('ip route show dev veth99 10.10.10.12')
3302            print(output)
3303            self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
3304
3305            output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
3306            print(output)
3307            self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
3308
3309            output = check_output('ip route show 10.10.10.13')
3310            print(output)
3311            self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
3312
3313            output = check_output('ip -6 route show 2001:1234:5:8f62::2')
3314            print(output)
3315            self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
3316
3317            output = check_output('ip route show 10.10.10.14')
3318            print(output)
3319            self.assertIn('10.10.10.14 nhid 21 proto static', output)
3320            self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
3321            self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
3322
3323            # TODO: check json string
3324            check_output(*networkctl_cmd, '--json=short', 'status', env=env)
3325
3326        copy_unit_to_networkd_unit_path('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
3327                                        '12-dummy.netdev', '25-nexthop-dummy.network')
3328        start_networkd()
3329
3330        check_nexthop(self)
3331
3332        remove_unit_from_networkd_path(['25-nexthop.network'])
3333        copy_unit_to_networkd_unit_path('25-nexthop-nothing.network')
3334        rc = call(*networkctl_cmd, 'reload', env=env)
3335        self.assertEqual(rc, 0)
3336        time.sleep(1)
3337
3338        self.wait_online(['veth99:routable', 'veth-peer:routable'])
3339
3340        output = check_output('ip nexthop list dev veth99')
3341        print(output)
3342        self.assertEqual(output, '')
3343        output = check_output('ip nexthop list dev lo')
3344        print(output)
3345        self.assertEqual(output, '')
3346
3347        remove_unit_from_networkd_path(['25-nexthop-nothing.network'])
3348        copy_unit_to_networkd_unit_path('25-nexthop.network')
3349        rc = call(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
3350        self.assertEqual(rc, 0)
3351        rc = call(*networkctl_cmd, 'reload', env=env)
3352        self.assertEqual(rc, 0)
3353        time.sleep(1)
3354
3355        check_nexthop(self)
3356
3357        rc = call('ip link del veth99')
3358        self.assertEqual(rc, 0)
3359        time.sleep(2)
3360
3361        output = check_output('ip nexthop list dev lo')
3362        print(output)
3363        self.assertEqual(output, '')
3364
3365    def test_qdisc(self):
3366        copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
3367                                        '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
3368        check_output('modprobe sch_teql max_equalizers=2')
3369        start_networkd()
3370
3371        self.wait_online(['dummy98:routable', 'test1:routable'])
3372
3373        output = check_output('tc qdisc show dev test1')
3374        print(output)
3375        self.assertRegex(output, 'qdisc netem')
3376        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
3377        self.assertRegex(output, 'qdisc ingress')
3378
3379        output = check_output('tc qdisc show dev dummy98')
3380        print(output)
3381        self.assertRegex(output, 'qdisc clsact')
3382
3383        self.assertRegex(output, 'qdisc htb 2: root')
3384        self.assertRegex(output, r'default (0x30|30)')
3385
3386        self.assertRegex(output, 'qdisc netem 30: parent 2:30')
3387        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
3388        self.assertRegex(output, 'qdisc fq_codel')
3389        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')
3390
3391        self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
3392
3393        self.assertRegex(output, 'qdisc fq 32: parent 2:32')
3394        self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
3395        self.assertRegex(output, 'quantum 1500')
3396        self.assertRegex(output, 'initial_quantum 13000')
3397        self.assertRegex(output, 'maxrate 1Mbit')
3398
3399        self.assertRegex(output, 'qdisc codel 33: parent 2:33')
3400        self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
3401
3402        self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
3403        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')
3404
3405        self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
3406        self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
3407
3408        self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
3409        self.assertRegex(output, 'perturb 5sec')
3410
3411        self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
3412        self.assertRegex(output, 'limit 100000p')
3413
3414        self.assertRegex(output, 'qdisc gred 38: parent 2:38')
3415        self.assertRegex(output, 'vqs 12 default 10 grio')
3416
3417        self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
3418        self.assertRegex(output, 'limit 200000')
3419
3420        self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
3421        self.assertRegex(output, 'limit 1000000')
3422
3423        self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
3424        self.assertRegex(output, 'limit 1023p')
3425
3426        self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
3427
3428        output = check_output('tc -d class show dev dummy98')
3429        print(output)
3430        self.assertRegex(output, 'class htb 2:30 root leaf 30:')
3431        self.assertRegex(output, 'class htb 2:31 root leaf 31:')
3432        self.assertRegex(output, 'class htb 2:32 root leaf 32:')
3433        self.assertRegex(output, 'class htb 2:33 root leaf 33:')
3434        self.assertRegex(output, 'class htb 2:34 root leaf 34:')
3435        self.assertRegex(output, 'class htb 2:35 root leaf 35:')
3436        self.assertRegex(output, 'class htb 2:36 root leaf 36:')
3437        self.assertRegex(output, 'class htb 2:37 root leaf 37:')
3438        self.assertRegex(output, 'class htb 2:38 root leaf 38:')
3439        self.assertRegex(output, 'class htb 2:39 root leaf 39:')
3440        self.assertRegex(output, 'class htb 2:3a root leaf 3a:')
3441        self.assertRegex(output, 'class htb 2:3b root leaf 3b:')
3442        self.assertRegex(output, 'class htb 2:3c root leaf 3c:')
3443        self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
3444        self.assertRegex(output, 'burst 123456')
3445        self.assertRegex(output, 'cburst 123457')
3446
3447    def test_qdisc2(self):
3448        copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev',
3449                                        '25-qdisc-qfq.network', '11-dummy.netdev')
3450        start_networkd()
3451
3452        self.wait_online(['dummy98:routable', 'test1:routable'])
3453
3454        output = check_output('tc qdisc show dev dummy98')
3455        print(output)
3456        self.assertRegex(output, 'qdisc drr 2: root')
3457        output = check_output('tc class show dev dummy98')
3458        print(output)
3459        self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
3460
3461        output = check_output('tc qdisc show dev test1')
3462        print(output)
3463        self.assertRegex(output, 'qdisc qfq 2: root')
3464        output = check_output('tc class show dev test1')
3465        print(output)
3466        self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
3467        self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
3468
3469    @expectedFailureIfCAKEIsNotAvailable()
3470    def test_qdisc_cake(self):
3471        copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')
3472        start_networkd()
3473        self.wait_online(['dummy98:routable'])
3474
3475        output = check_output('tc qdisc show dev dummy98')
3476        print(output)
3477        self.assertIn('qdisc cake 3a: root', output)
3478        self.assertIn('bandwidth 500Mbit', output)
3479        self.assertIn('autorate-ingress', output)
3480        self.assertIn('diffserv8', output)
3481        self.assertIn('dual-dsthost', output)
3482        self.assertIn(' nat', output)
3483        self.assertIn(' wash', output)
3484        self.assertIn(' split-gso', output)
3485        self.assertIn(' raw', output)
3486        self.assertIn(' atm', output)
3487        self.assertIn('overhead 128', output)
3488        self.assertIn('mpu 20', output)
3489        self.assertIn('fwmark 0xff00', output)
3490
3491    @expectedFailureIfPIEIsNotAvailable()
3492    def test_qdisc_pie(self):
3493        copy_unit_to_networkd_unit_path('25-qdisc-pie.network', '12-dummy.netdev')
3494        start_networkd()
3495        self.wait_online(['dummy98:routable'])
3496
3497        output = check_output('tc qdisc show dev dummy98')
3498        print(output)
3499        self.assertRegex(output, 'qdisc pie 3a: root')
3500        self.assertRegex(output, 'limit 200000')
3501
3502    @expectedFailureIfHHFIsNotAvailable()
3503    def test_qdisc_hhf(self):
3504        copy_unit_to_networkd_unit_path('25-qdisc-hhf.network', '12-dummy.netdev')
3505        start_networkd()
3506        self.wait_online(['dummy98:routable'])
3507
3508        output = check_output('tc qdisc show dev dummy98')
3509        print(output)
3510        self.assertRegex(output, 'qdisc hhf 3a: root')
3511        self.assertRegex(output, 'limit 1022p')
3512
3513    @expectedFailureIfETSIsNotAvailable()
3514    def test_qdisc_ets(self):
3515        copy_unit_to_networkd_unit_path('25-qdisc-ets.network', '12-dummy.netdev')
3516        start_networkd()
3517        self.wait_online(['dummy98:routable'])
3518
3519        output = check_output('tc qdisc show dev dummy98')
3520        print(output)
3521
3522        self.assertRegex(output, 'qdisc ets 3a: root')
3523        self.assertRegex(output, 'bands 10 strict 3')
3524        self.assertRegex(output, 'quanta 1 2 3 4 5')
3525        self.assertRegex(output, 'priomap 3 4 5 6 7')
3526
3527    @expectedFailureIfFQPIEIsNotAvailable()
3528    def test_qdisc_fq_pie(self):
3529        copy_unit_to_networkd_unit_path('25-qdisc-fq_pie.network', '12-dummy.netdev')
3530        start_networkd()
3531        self.wait_online(['dummy98:routable'])
3532
3533        output = check_output('tc qdisc show dev dummy98')
3534        print(output)
3535
3536        self.assertRegex(output, 'qdisc fq_pie 3a: root')
3537        self.assertRegex(output, 'limit 200000p')
3538
3539    def test_wait_online_ipv4(self):
3540        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-with-ipv6-prefix.network', '25-dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
3541        start_networkd()
3542
3543        self.wait_online(['veth99:routable'], ipv4=True)
3544
3545        self.wait_address('veth99', r'192.168.5.[0-9]+', ipv='-4', timeout_sec=1)
3546
3547    def test_wait_online_ipv6(self):
3548        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6-prefix-with-delay.network', '25-ipv6ra-prefix-client-with-static-ipv4-address.network')
3549        start_networkd()
3550
3551        self.wait_online(['veth99:routable'], ipv6=True)
3552
3553        self.wait_address('veth99', r'2002:da8:1:0:1034:56ff:fe78:9abc', ipv='-6', timeout_sec=1)
3554
3555class NetworkdStateFileTests(unittest.TestCase, Utilities):
3556    links = [
3557        'dummy98',
3558    ]
3559
3560    units = [
3561        '12-dummy.netdev',
3562        '25-state-file-tests.network',
3563    ]
3564
3565    def setUp(self):
3566        remove_links(self.links)
3567        stop_networkd(show_logs=False)
3568
3569    def tearDown(self):
3570        remove_links(self.links)
3571        remove_unit_from_networkd_path(self.units)
3572        stop_networkd(show_logs=True)
3573
3574    def test_state_file(self):
3575        copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-state-file-tests.network')
3576        start_networkd()
3577        self.wait_online(['dummy98:routable'])
3578
3579        output = check_output(*networkctl_cmd, '--no-legend', 'list', 'dummy98', env=env)
3580        print(output)
3581        ifindex = output.split()[0]
3582
3583        path = os.path.join('/run/systemd/netif/links/', ifindex)
3584        self.assertTrue(os.path.exists(path))
3585
3586        # make link state file updated
3587        check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
3588
3589        # TODO: check json string
3590        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
3591
3592        with open(path) as f:
3593            data = f.read()
3594            self.assertRegex(data, r'IPV4_ADDRESS_STATE=routable')
3595            self.assertRegex(data, r'IPV6_ADDRESS_STATE=routable')
3596            self.assertRegex(data, r'ADMIN_STATE=configured')
3597            self.assertRegex(data, r'OPER_STATE=routable')
3598            self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
3599            self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
3600            self.assertRegex(data, r'REQUIRED_FAMILY_FOR_ONLINE=both')
3601            self.assertRegex(data, r'ACTIVATION_POLICY=up')
3602            self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/25-state-file-tests.network')
3603            self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
3604            self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
3605            self.assertRegex(data, r'DOMAINS=hogehoge')
3606            self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
3607            self.assertRegex(data, r'LLMNR=no')
3608            self.assertRegex(data, r'MDNS=yes')
3609            self.assertRegex(data, r'DNSSEC=no')
3610
3611        check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env)
3612        check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
3613        check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
3614        check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
3615        check_output(*resolvectl_cmd, 'dnssec', 'dummy98', 'yes', env=env)
3616        check_output(*timedatectl_cmd, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env=env)
3617
3618        # TODO: check json string
3619        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
3620
3621        with open(path) as f:
3622            data = f.read()
3623            self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
3624            self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
3625            self.assertRegex(data, r'DOMAINS=hogehogehoge')
3626            self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
3627            self.assertRegex(data, r'LLMNR=yes')
3628            self.assertRegex(data, r'MDNS=no')
3629            self.assertRegex(data, r'DNSSEC=yes')
3630
3631        check_output(*timedatectl_cmd, 'revert', 'dummy98', env=env)
3632
3633        # TODO: check json string
3634        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
3635
3636        with open(path) as f:
3637            data = f.read()
3638            self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
3639            self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
3640            self.assertRegex(data, r'DOMAINS=hogehogehoge')
3641            self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
3642            self.assertRegex(data, r'LLMNR=yes')
3643            self.assertRegex(data, r'MDNS=no')
3644            self.assertRegex(data, r'DNSSEC=yes')
3645
3646        check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
3647
3648        # TODO: check json string
3649        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
3650
3651        with open(path) as f:
3652            data = f.read()
3653            self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
3654            self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
3655            self.assertRegex(data, r'DOMAINS=hogehoge')
3656            self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
3657            self.assertRegex(data, r'LLMNR=no')
3658            self.assertRegex(data, r'MDNS=yes')
3659            self.assertRegex(data, r'DNSSEC=no')
3660
3661class NetworkdBondTests(unittest.TestCase, Utilities):
3662    links = [
3663        'bond199',
3664        'bond99',
3665        'dummy98',
3666        'test1']
3667
3668    units = [
3669        '11-dummy.netdev',
3670        '12-dummy.netdev',
3671        '23-active-slave.network',
3672        '23-bond199.network',
3673        '23-keep-master.network',
3674        '23-primary-slave.network',
3675        '25-bond-active-backup-slave.netdev',
3676        '25-bond.netdev',
3677        '25-bond99.network',
3678        '25-bond-slave.network']
3679
3680    def setUp(self):
3681        remove_links(self.links)
3682        stop_networkd(show_logs=False)
3683
3684    def tearDown(self):
3685        remove_links(self.links)
3686        remove_unit_from_networkd_path(self.units)
3687        stop_networkd(show_logs=True)
3688
3689    def test_bond_keep_master(self):
3690        check_output('ip link add bond199 type bond mode active-backup')
3691        check_output('ip link add dummy98 type dummy')
3692        check_output('ip link set dummy98 master bond199')
3693
3694        copy_unit_to_networkd_unit_path('23-keep-master.network')
3695        start_networkd()
3696        self.wait_online(['dummy98:enslaved'])
3697
3698        output = check_output('ip -d link show bond199')
3699        print(output)
3700        self.assertRegex(output, 'active_slave dummy98')
3701
3702        output = check_output('ip -d link show dummy98')
3703        print(output)
3704        self.assertRegex(output, 'master bond199')
3705
3706    def test_bond_active_slave(self):
3707        copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3708        start_networkd()
3709        self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
3710
3711        output = check_output('ip -d link show bond199')
3712        print(output)
3713        self.assertRegex(output, 'active_slave dummy98')
3714
3715    def test_bond_primary_slave(self):
3716        copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3717        start_networkd()
3718        self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
3719
3720        output = check_output('ip -d link show bond199')
3721        print(output)
3722        self.assertRegex(output, 'primary dummy98')
3723
3724    def test_bond_operstate(self):
3725        copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
3726                                        '25-bond99.network','25-bond-slave.network')
3727        start_networkd()
3728        self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
3729
3730        output = check_output('ip -d link show dummy98')
3731        print(output)
3732        self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
3733
3734        output = check_output('ip -d link show test1')
3735        print(output)
3736        self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
3737
3738        output = check_output('ip -d link show bond99')
3739        print(output)
3740        self.assertRegex(output, 'MASTER,UP,LOWER_UP')
3741
3742        self.wait_operstate('dummy98', 'enslaved')
3743        self.wait_operstate('test1', 'enslaved')
3744        self.wait_operstate('bond99', 'routable')
3745
3746        check_output('ip link set dummy98 down')
3747
3748        self.wait_operstate('dummy98', 'off')
3749        self.wait_operstate('test1', 'enslaved')
3750        self.wait_operstate('bond99', 'degraded-carrier')
3751
3752        check_output('ip link set dummy98 up')
3753
3754        self.wait_operstate('dummy98', 'enslaved')
3755        self.wait_operstate('test1', 'enslaved')
3756        self.wait_operstate('bond99', 'routable')
3757
3758        check_output('ip link set dummy98 down')
3759        check_output('ip link set test1 down')
3760
3761        self.wait_operstate('dummy98', 'off')
3762        self.wait_operstate('test1', 'off')
3763
3764        if not self.wait_operstate('bond99', 'no-carrier', setup_timeout=30, fail_assert=False):
3765            # Huh? Kernel does not recognize that all slave interfaces are down?
3766            # Let's confirm that networkd's operstate is consistent with ip's result.
3767            self.assertNotRegex(output, 'NO-CARRIER')
3768
3769class NetworkdBridgeTests(unittest.TestCase, Utilities):
3770    links = [
3771        'bridge99',
3772        'dummy98',
3773        'test1',
3774        'vlan99',
3775    ]
3776
3777    units = [
3778        '11-dummy.netdev',
3779        '12-dummy.netdev',
3780        '21-vlan.netdev',
3781        '21-vlan.network',
3782        '23-keep-master.network',
3783        '26-bridge.netdev',
3784        '26-bridge-configure-without-carrier.network',
3785        '26-bridge-issue-20373.netdev',
3786        '26-bridge-mdb-master.network',
3787        '26-bridge-mdb-slave.network',
3788        '26-bridge-slave-interface-1.network',
3789        '26-bridge-slave-interface-2.network',
3790        '26-bridge-vlan-master-issue-20373.network',
3791        '26-bridge-vlan-master.network',
3792        '26-bridge-vlan-slave-issue-20373.network',
3793        '26-bridge-vlan-slave.network',
3794        '25-bridge99-ignore-carrier-loss.network',
3795        '25-bridge99.network'
3796    ]
3797
3798    routing_policy_rule_tables = ['100']
3799
3800    def setUp(self):
3801        remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
3802        remove_links(self.links)
3803        stop_networkd(show_logs=False)
3804
3805    def tearDown(self):
3806        remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
3807        remove_links(self.links)
3808        remove_unit_from_networkd_path(self.units)
3809        stop_networkd(show_logs=True)
3810
3811    def test_bridge_vlan(self):
3812        copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave.network',
3813                                        '26-bridge.netdev', '26-bridge-vlan-master.network')
3814        start_networkd()
3815        self.wait_online(['test1:enslaved', 'bridge99:degraded'])
3816
3817        output = check_output('bridge vlan show dev test1')
3818        print(output)
3819        self.assertNotRegex(output, '4063')
3820        for i in range(4064, 4095):
3821            self.assertRegex(output, f'{i}')
3822        self.assertNotRegex(output, '4095')
3823
3824        output = check_output('bridge vlan show dev bridge99')
3825        print(output)
3826        self.assertNotRegex(output, '4059')
3827        for i in range(4060, 4095):
3828            self.assertRegex(output, f'{i}')
3829        self.assertNotRegex(output, '4095')
3830
3831    def test_bridge_vlan_issue_20373(self):
3832        copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
3833                                        '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
3834                                        '21-vlan.netdev', '21-vlan.network')
3835        start_networkd()
3836        self.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
3837
3838        output = check_output('bridge vlan show dev test1')
3839        print(output)
3840        self.assertIn('100 PVID Egress Untagged', output)
3841        self.assertIn('560', output)
3842        self.assertIn('600', output)
3843
3844        output = check_output('bridge vlan show dev bridge99')
3845        print(output)
3846        self.assertIn('1 PVID Egress Untagged', output)
3847        self.assertIn('100', output)
3848        self.assertIn('600', output)
3849
3850    def test_bridge_mdb(self):
3851        copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-mdb-slave.network',
3852                                        '26-bridge.netdev', '26-bridge-mdb-master.network')
3853        start_networkd()
3854        self.wait_online(['test1:enslaved', 'bridge99:degraded'])
3855
3856        output = check_output('bridge mdb show dev bridge99')
3857        print(output)
3858        self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
3859        self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
3860
3861        # Old kernel may not support bridge MDB entries on bridge master
3862        if call('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068', stderr=subprocess.DEVNULL) == 0:
3863            self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
3864            self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
3865
3866    def test_bridge_keep_master(self):
3867        check_output('ip link add bridge99 type bridge')
3868        check_output('ip link set bridge99 up')
3869        check_output('ip link add dummy98 type dummy')
3870        check_output('ip link set dummy98 master bridge99')
3871
3872        copy_unit_to_networkd_unit_path('23-keep-master.network')
3873        start_networkd()
3874        self.wait_online(['dummy98:enslaved'])
3875
3876        output = check_output('ip -d link show dummy98')
3877        print(output)
3878        self.assertRegex(output, 'master bridge99')
3879        self.assertRegex(output, 'bridge')
3880
3881        output = check_output('bridge -d link show dummy98')
3882        print(output)
3883        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
3884        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
3885        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
3886        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
3887        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
3888        # CONFIG_BRIDGE_IGMP_SNOOPING=y
3889        if os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast'):
3890            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
3891        if os.path.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress'):
3892            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
3893        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
3894        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'priority'), '23')
3895        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard'), '1')
3896        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'root_block'), '1')
3897
3898    def test_bridge_property(self):
3899        copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
3900                                        '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
3901                                        '25-bridge99.network')
3902        start_networkd()
3903        self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
3904
3905        output = check_output('ip -d link show test1')
3906        print(output)
3907        self.assertRegex(output, 'master')
3908        self.assertRegex(output, 'bridge')
3909
3910        output = check_output('ip -d link show dummy98')
3911        print(output)
3912        self.assertRegex(output, 'master')
3913        self.assertRegex(output, 'bridge')
3914
3915        output = check_output('ip addr show bridge99')
3916        print(output)
3917        self.assertRegex(output, '192.168.0.15/24')
3918
3919        output = check_output('bridge -d link show dummy98')
3920        print(output)
3921        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
3922        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
3923        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'isolated'), '1')
3924        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
3925        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
3926        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
3927        # CONFIG_BRIDGE_IGMP_SNOOPING=y
3928        if os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast'):
3929            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
3930        if os.path.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress'):
3931            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
3932        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
3933        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'priority'), '23')
3934        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard'), '1')
3935        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'root_block'), '1')
3936
3937        output = check_output('bridge -d link show test1')
3938        print(output)
3939        self.assertEqual(read_bridge_port_attr('bridge99', 'test1', 'priority'), '0')
3940
3941        check_output('ip address add 192.168.0.16/24 dev bridge99')
3942        time.sleep(1)
3943
3944        output = check_output('ip addr show bridge99')
3945        print(output)
3946        self.assertRegex(output, '192.168.0.16/24')
3947
3948        # for issue #6088
3949        print('### ip -6 route list table all dev bridge99')
3950        output = check_output('ip -6 route list table all dev bridge99')
3951        print(output)
3952        self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
3953
3954        self.assertEqual(call('ip link del test1'), 0)
3955
3956        self.wait_operstate('bridge99', 'degraded-carrier')
3957
3958        check_output('ip link del dummy98')
3959
3960        self.wait_operstate('bridge99', 'no-carrier')
3961
3962        output = check_output('ip address show bridge99')
3963        print(output)
3964        self.assertRegex(output, 'NO-CARRIER')
3965        self.assertNotRegex(output, '192.168.0.15/24')
3966        self.assertRegex(output, '192.168.0.16/24') # foreign address is kept
3967
3968        print('### ip -6 route list table all dev bridge99')
3969        output = check_output('ip -6 route list table all dev bridge99')
3970        print(output)
3971        self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
3972
3973    def test_bridge_configure_without_carrier(self):
3974        copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
3975                                        '11-dummy.netdev')
3976        start_networkd()
3977
3978        # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
3979        for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
3980            with self.subTest(test=test):
3981                if test == 'no-slave':
3982                    # bridge has no slaves; it's up but *might* not have carrier
3983                    self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
3984                    # due to a bug in the kernel, newly-created bridges are brought up
3985                    # *with* carrier, unless they have had any setting changed; e.g.
3986                    # their mac set, priority set, etc.  Then, they will lose carrier
3987                    # as soon as a (down) slave interface is added, and regain carrier
3988                    # again once the slave interface is brought up.
3989                    #self.check_link_attr('bridge99', 'carrier', '0')
3990                elif test == 'add-slave':
3991                    # add slave to bridge, but leave it down; bridge is definitely no-carrier
3992                    self.check_link_attr('test1', 'operstate', 'down')
3993                    check_output('ip link set dev test1 master bridge99')
3994                    self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
3995                    self.check_link_attr('bridge99', 'carrier', '0')
3996                elif test == 'slave-up':
3997                    # bring up slave, which will have carrier; bridge gains carrier
3998                    check_output('ip link set dev test1 up')
3999                    self.wait_online(['bridge99:routable'])
4000                    self.check_link_attr('bridge99', 'carrier', '1')
4001                elif test == 'slave-no-carrier':
4002                    # drop slave carrier; bridge loses carrier
4003                    check_output('ip link set dev test1 carrier off')
4004                    self.wait_online(['bridge99:no-carrier:no-carrier'])
4005                    self.check_link_attr('bridge99', 'carrier', '0')
4006                elif test == 'slave-carrier':
4007                    # restore slave carrier; bridge gains carrier
4008                    check_output('ip link set dev test1 carrier on')
4009                    self.wait_online(['bridge99:routable'])
4010                    self.check_link_attr('bridge99', 'carrier', '1')
4011                elif test == 'slave-down':
4012                    # bring down slave; bridge loses carrier
4013                    check_output('ip link set dev test1 down')
4014                    self.wait_online(['bridge99:no-carrier:no-carrier'])
4015                    self.check_link_attr('bridge99', 'carrier', '0')
4016
4017                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env)
4018                self.assertRegex(output, '10.1.2.3')
4019                self.assertRegex(output, '10.1.2.1')
4020
4021    def test_bridge_ignore_carrier_loss(self):
4022        copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4023                                        '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4024                                        '25-bridge99-ignore-carrier-loss.network')
4025        start_networkd()
4026        self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
4027
4028        check_output('ip address add 192.168.0.16/24 dev bridge99')
4029        time.sleep(1)
4030
4031        check_output('ip link del test1')
4032        check_output('ip link del dummy98')
4033        time.sleep(3)
4034
4035        output = check_output('ip address show bridge99')
4036        print(output)
4037        self.assertRegex(output, 'NO-CARRIER')
4038        self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4039        self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
4040
4041    def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
4042        copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network',
4043                                        '25-bridge99-ignore-carrier-loss.network')
4044        start_networkd()
4045        self.wait_online(['bridge99:no-carrier'])
4046
4047        for trial in range(4):
4048            check_output('ip link add dummy98 type dummy')
4049            check_output('ip link set dummy98 up')
4050            if trial < 3:
4051                check_output('ip link del dummy98')
4052
4053        self.wait_online(['bridge99:routable', 'dummy98:enslaved'])
4054
4055        output = check_output('ip address show bridge99')
4056        print(output)
4057        self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
4058
4059        output = check_output('ip rule list table 100')
4060        print(output)
4061        self.assertIn('from all to 8.8.8.8 lookup 100', output)
4062
4063class NetworkdSRIOVTests(unittest.TestCase, Utilities):
4064    units = [
4065        '25-sriov-udev.network',
4066        '25-sriov.link',
4067        '25-sriov.network',
4068    ]
4069
4070    def setUp(self):
4071        stop_networkd(show_logs=False)
4072        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
4073
4074    def tearDown(self):
4075        remove_unit_from_networkd_path(self.units)
4076        stop_networkd(show_logs=True)
4077        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
4078
4079    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4080    def test_sriov(self):
4081        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
4082
4083        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
4084            f.write('99 1')
4085
4086        call('udevadm settle')
4087        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
4088        with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
4089            f.write('3')
4090
4091        copy_unit_to_networkd_unit_path('25-sriov.network')
4092        start_networkd()
4093        self.wait_online(['eni99np1:routable'])
4094
4095        output = check_output('ip link show dev eni99np1')
4096        print(output)
4097        self.assertRegex(output,
4098                         '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 *'
4099                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4100                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4101        )
4102
4103    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
4104    def test_sriov_udev(self):
4105        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
4106
4107        copy_unit_to_networkd_unit_path('25-sriov.link', '25-sriov-udev.network')
4108        call('udevadm control --reload')
4109
4110        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
4111            f.write('99 1')
4112
4113        start_networkd()
4114        self.wait_online(['eni99np1:routable'])
4115
4116        output = check_output('ip link show dev eni99np1')
4117        print(output)
4118        self.assertRegex(output,
4119                         '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 *'
4120                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4121                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4122        )
4123        self.assertNotIn('vf 3', output)
4124        self.assertNotIn('vf 4', output)
4125
4126        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
4127            f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
4128
4129        call('udevadm control --reload')
4130        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
4131
4132        output = check_output('ip link show dev eni99np1')
4133        print(output)
4134        self.assertRegex(output,
4135                         '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 *'
4136                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4137                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4138                         'vf 3'
4139        )
4140        self.assertNotIn('vf 4', output)
4141
4142        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
4143            f.write('[Link]\nSR-IOVVirtualFunctions=\n')
4144
4145        call('udevadm control --reload')
4146        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
4147
4148        output = check_output('ip link show dev eni99np1')
4149        print(output)
4150        self.assertRegex(output,
4151                         '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 *'
4152                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4153                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
4154                         'vf 3'
4155        )
4156        self.assertNotIn('vf 4', output)
4157
4158        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
4159            f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
4160
4161        call('udevadm control --reload')
4162        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
4163
4164        output = check_output('ip link show dev eni99np1')
4165        print(output)
4166        self.assertRegex(output,
4167                         '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 *'
4168                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
4169        )
4170        self.assertNotIn('vf 2', output)
4171        self.assertNotIn('vf 3', output)
4172        self.assertNotIn('vf 4', output)
4173
4174        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
4175            f.write('[Link]\nSR-IOVVirtualFunctions=\n')
4176
4177        call('udevadm control --reload')
4178        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
4179
4180        output = check_output('ip link show dev eni99np1')
4181        print(output)
4182        self.assertRegex(output,
4183                         '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 *'
4184                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
4185                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
4186        )
4187        self.assertNotIn('vf 3', output)
4188        self.assertNotIn('vf 4', output)
4189
4190class NetworkdLLDPTests(unittest.TestCase, Utilities):
4191    links = ['veth99']
4192
4193    units = [
4194        '23-emit-lldp.network',
4195        '24-lldp.network',
4196        '25-veth.netdev']
4197
4198    def setUp(self):
4199        remove_links(self.links)
4200        stop_networkd(show_logs=False)
4201
4202    def tearDown(self):
4203        remove_links(self.links)
4204        remove_unit_from_networkd_path(self.units)
4205        stop_networkd(show_logs=True)
4206
4207    def test_lldp(self):
4208        copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
4209        start_networkd()
4210        self.wait_online(['veth99:degraded', 'veth-peer:degraded'])
4211
4212        for trial in range(10):
4213            if trial > 0:
4214                time.sleep(1)
4215
4216            output = check_output(*networkctl_cmd, 'lldp', env=env)
4217            print(output)
4218            if re.search(r'veth99 .* veth-peer', output):
4219                break
4220        else:
4221            self.fail()
4222
4223class NetworkdRATests(unittest.TestCase, Utilities):
4224    links = ['veth99']
4225
4226    units = [
4227        '25-veth.netdev',
4228        '25-ipv6-prefix.network',
4229        '25-ipv6-prefix-veth.network',
4230        '25-ipv6-prefix-veth-token-static.network',
4231        '25-ipv6-prefix-veth-token-prefixstable.network',
4232        '25-ipv6-prefix-veth-token-prefixstable-without-address.network']
4233
4234    def setUp(self):
4235        remove_links(self.links)
4236        stop_networkd(show_logs=False)
4237
4238    def tearDown(self):
4239        remove_links(self.links)
4240        remove_unit_from_networkd_path(self.units)
4241        stop_networkd(show_logs=True)
4242
4243    def test_ipv6_prefix_delegation(self):
4244        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
4245        start_networkd()
4246        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
4247
4248        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
4249        print(output)
4250        self.assertRegex(output, 'fe80::')
4251        self.assertRegex(output, '2002:da8:1::1')
4252
4253        output = check_output(*resolvectl_cmd, 'domain', 'veth99', env=env)
4254        print(output)
4255        self.assertIn('hogehoge.test', output)
4256
4257        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4258        print(output)
4259        self.assertRegex(output, '2002:da8:1:0')
4260
4261    def test_ipv6_token_static(self):
4262        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
4263        start_networkd()
4264        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
4265
4266        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4267        print(output)
4268        self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
4269        self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
4270        self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
4271        self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
4272
4273    def test_ipv6_token_prefixstable(self):
4274        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
4275        start_networkd()
4276        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
4277
4278        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4279        print(output)
4280        self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
4281        self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
4282
4283    def test_ipv6_token_prefixstable_without_address(self):
4284        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
4285        start_networkd()
4286        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
4287
4288        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4289        print(output)
4290        self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
4291        self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
4292
4293class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
4294    links = [
4295        'dummy98',
4296        'veth99',
4297    ]
4298
4299    units = [
4300        '12-dummy.netdev',
4301        '25-veth.netdev',
4302        '25-dhcp-client.network',
4303        '25-dhcp-client-static-lease.network',
4304        '25-dhcp-client-timezone-router.network',
4305        '25-dhcp-server.network',
4306        '25-dhcp-server-downstream.network',
4307        '25-dhcp-server-static-lease.network',
4308        '25-dhcp-server-timezone-router.network',
4309        '25-dhcp-server-uplink.network',
4310    ]
4311
4312    def setUp(self):
4313        remove_links(self.links)
4314        stop_networkd(show_logs=False)
4315
4316    def tearDown(self):
4317        remove_links(self.links)
4318        remove_unit_from_networkd_path(self.units)
4319        stop_networkd(show_logs=True)
4320
4321    def test_dhcp_server(self):
4322        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
4323        start_networkd()
4324        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4325
4326        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4327        print(output)
4328        self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
4329        self.assertIn('Gateway: 192.168.5.3', output)
4330        self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
4331        self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
4332
4333    def test_dhcp_server_with_uplink(self):
4334        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
4335                                        '12-dummy.netdev', '25-dhcp-server-uplink.network')
4336        start_networkd()
4337        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4338
4339        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4340        print(output)
4341        self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
4342        self.assertIn('Gateway: 192.168.5.3', output)
4343        self.assertIn('DNS: 192.168.5.1', output)
4344        self.assertIn('NTP: 192.168.5.1', output)
4345
4346    def test_emit_router_timezone(self):
4347        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
4348        start_networkd()
4349        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4350
4351        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4352        print(output)
4353        self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
4354        self.assertIn('Gateway: 192.168.5.1', output)
4355        self.assertIn('Time Zone: Europe/Berlin', output)
4356
4357    def test_dhcp_server_static_lease(self):
4358        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
4359        start_networkd()
4360        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4361
4362        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4363        print(output)
4364        self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
4365
4366class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
4367    links = [
4368        'client',
4369        'server',
4370        'client-peer',
4371        'server-peer',
4372        ]
4373
4374    units = [
4375        '25-agent-veth-client.netdev',
4376        '25-agent-veth-server.netdev',
4377        '25-agent-client.network',
4378        '25-agent-server.network',
4379        '25-agent-client-peer.network',
4380        '25-agent-server-peer.network',
4381        ]
4382
4383    def setUp(self):
4384        remove_links(self.links)
4385        stop_networkd(show_logs=False)
4386
4387    def tearDown(self):
4388        remove_links(self.links)
4389        remove_unit_from_networkd_path(self.units)
4390        stop_networkd(show_logs=True)
4391
4392    def test_relay_agent(self):
4393        copy_unit_to_networkd_unit_path(*self.units)
4394        start_networkd()
4395
4396        self.wait_online(['client:routable'])
4397
4398        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'client', env=env)
4399        print(output)
4400        self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
4401
4402class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
4403    links = [
4404        'veth99',
4405        'vrf99']
4406
4407    units = [
4408        '25-veth.netdev',
4409        '25-vrf.netdev',
4410        '25-vrf.network',
4411        '25-dhcp-client-allow-list.network',
4412        '25-dhcp-client-anonymize.network',
4413        '25-dhcp-client-decline.network',
4414        '25-dhcp-client-gateway-ipv4.network',
4415        '25-dhcp-client-gateway-ipv6.network',
4416        '25-dhcp-client-gateway-onlink-implicit.network',
4417        '25-dhcp-client-ipv4-dhcp-settings.network',
4418        '25-dhcp-client-ipv4-only-ipv6-disabled.network',
4419        '25-dhcp-client-ipv4-only.network',
4420        '25-dhcp-client-ipv4-use-routes-use-gateway.network',
4421        '25-dhcp-client-ipv6-only.network',
4422        '25-dhcp-client-keep-configuration-dhcp-on-stop.network',
4423        '25-dhcp-client-keep-configuration-dhcp.network',
4424        '25-dhcp-client-listen-port.network',
4425        '25-dhcp-client-reassign-static-routes-ipv4.network',
4426        '25-dhcp-client-reassign-static-routes-ipv6.network',
4427        '25-dhcp-client-route-metric.network',
4428        '25-dhcp-client-route-table.network',
4429        '25-dhcp-client-use-dns-ipv4-and-ra.network',
4430        '25-dhcp-client-use-dns-ipv4.network',
4431        '25-dhcp-client-use-dns-no.network',
4432        '25-dhcp-client-use-dns-yes.network',
4433        '25-dhcp-client-use-domains.network',
4434        '25-dhcp-client-vrf.network',
4435        '25-dhcp-client-with-ipv4ll.network',
4436        '25-dhcp-client-with-static-address.network',
4437        '25-dhcp-client.network',
4438        '25-dhcp-server-decline.network',
4439        '25-dhcp-server-veth-peer.network',
4440        '25-dhcp-v4-server-veth-peer.network',
4441        '25-static.network']
4442
4443    def setUp(self):
4444        stop_dnsmasq()
4445        remove_dnsmasq_lease_file()
4446        remove_dnsmasq_log_file()
4447        remove_links(self.links)
4448        stop_networkd(show_logs=False)
4449
4450    def tearDown(self):
4451        stop_dnsmasq()
4452        remove_dnsmasq_lease_file()
4453        remove_dnsmasq_log_file()
4454        remove_links(self.links)
4455        remove_unit_from_networkd_path(self.units)
4456        stop_networkd(show_logs=True)
4457
4458    def test_dhcp_client_ipv6_only(self):
4459        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
4460
4461        start_networkd()
4462        self.wait_online(['veth-peer:carrier'])
4463        start_dnsmasq()
4464        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4465
4466        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4467        print(output)
4468        self.assertRegex(output, '2600::')
4469        self.assertNotRegex(output, '192.168.5')
4470
4471        output = check_output('ip addr show dev veth99')
4472        print(output)
4473        self.assertRegex(output, '2600::')
4474        self.assertNotRegex(output, '192.168.5')
4475        self.assertNotRegex(output, 'tentative')
4476
4477        # Confirm that ipv6 token is not set in the kernel
4478        output = check_output('ip token show dev veth99')
4479        print(output)
4480        self.assertRegex(output, 'token :: dev veth99')
4481
4482    def test_dhcp_client_ipv4_only(self):
4483        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only-ipv6-disabled.network')
4484
4485        start_networkd()
4486        self.wait_online(['veth-peer:carrier'])
4487        start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time='2m')
4488        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4489
4490        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4491        print(output)
4492        self.assertNotRegex(output, '2600::')
4493        self.assertRegex(output, '192.168.5')
4494        self.assertRegex(output, '192.168.5.6')
4495        self.assertRegex(output, '192.168.5.7')
4496
4497        # checking routes to DNS servers
4498        output = check_output('ip route show dev veth99')
4499        print(output)
4500        self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
4501        self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
4502        self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
4503
4504        stop_dnsmasq()
4505        start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', lease_time='2m')
4506
4507        # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
4508        print('Wait for the dynamic address to be renewed')
4509        time.sleep(125)
4510
4511        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4512
4513        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4514        print(output)
4515        self.assertNotRegex(output, '2600::')
4516        self.assertRegex(output, '192.168.5')
4517        self.assertNotRegex(output, '192.168.5.6')
4518        self.assertRegex(output, '192.168.5.7')
4519        self.assertRegex(output, '192.168.5.8')
4520
4521        # checking routes to DNS servers
4522        output = check_output('ip route show dev veth99')
4523        print(output)
4524        self.assertNotRegex(output, r'192.168.5.6')
4525        self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
4526        self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
4527        self.assertRegex(output, r'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024')
4528
4529    def test_dhcp_client_ipv4_use_routes_gateway(self):
4530        for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4):
4531            self.setUp()
4532            with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless):
4533                self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless)
4534            self.tearDown()
4535
4536    def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless):
4537        testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network'
4538        testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit]
4539        testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf')
4540        testunits.append(f'{testunit}.d/use-gateway-{use_gateway}.conf')
4541        testunits.append(f'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
4542        copy_unit_to_networkd_unit_path(*testunits, dropins=False)
4543
4544        start_networkd()
4545        self.wait_online(['veth-peer:carrier'])
4546        additional_options = '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8 --dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9 --dhcp-option=option:static-route,192.168.5.100,192.168.5.2,8.8.8.8,192.168.5.3'
4547        if classless:
4548            additional_options += ' --dhcp-option=option:classless-static-route,0.0.0.0/0,192.168.5.4,8.0.0.0/8,192.168.5.5'
4549        start_dnsmasq(additional_options=additional_options, lease_time='2m')
4550        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4551
4552        output = check_output('ip -4 route show dev veth99')
4553        print(output)
4554
4555        # Check UseRoutes=
4556        if use_routes:
4557            if classless:
4558                self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
4559                self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
4560                self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4561                self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4562            else:
4563                self.assertRegex(output, r'192.168.5.0/24 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4564                self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
4565                self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4566        else:
4567            self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
4568            self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
4569            self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4570            self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4571            self.assertNotRegex(output, r'192.168.5.0/24 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4572            self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
4573            self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4574
4575        # Check UseGateway=
4576        if use_gateway and (not classless or not use_routes):
4577            self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
4578        else:
4579            self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
4580
4581        # Check route to gateway
4582        if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes):
4583            self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4584        else:
4585            self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4586
4587        # Check RoutesToDNS= and RoutesToNTP=
4588        if dns_and_ntp_routes:
4589            self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4590            self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4591            if classless and use_routes:
4592                self.assertRegex(output, r'8.8.8.8 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
4593                self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
4594            else:
4595                self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
4596                self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
4597        else:
4598            self.assertNotRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4599            self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4600            self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
4601            self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
4602
4603        # TODO: check json string
4604        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
4605
4606    def test_dhcp_client_ipv4_ipv6(self):
4607        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
4608        start_networkd()
4609        self.wait_online(['veth-peer:carrier'])
4610        start_dnsmasq()
4611        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4612
4613        # link become 'routable' when at least one protocol provide an valid address.
4614        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
4615        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
4616
4617        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4618        print(output)
4619        self.assertRegex(output, '2600::')
4620        self.assertRegex(output, '192.168.5')
4621
4622    def test_dhcp_client_settings(self):
4623        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-dhcp-settings.network')
4624
4625        start_networkd()
4626        self.wait_online(['veth-peer:carrier'])
4627        start_dnsmasq()
4628        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4629
4630        print('## ip address show dev veth99')
4631        output = check_output('ip address show dev veth99')
4632        print(output)
4633        self.assertRegex(output, '12:34:56:78:9a:bc')
4634        self.assertRegex(output, '192.168.5')
4635        self.assertRegex(output, '1492')
4636        self.assertRegex(output, 'test-label')
4637
4638        print('## ip route show table main dev veth99')
4639        output = check_output('ip route show table main dev veth99')
4640        print(output)
4641        # See issue #8726
4642        main_table_is_empty = output == ''
4643        if not main_table_is_empty:
4644            self.assertNotRegex(output, 'proto dhcp')
4645
4646        print('## ip route show table 211 dev veth99')
4647        output = check_output('ip route show table 211 dev veth99')
4648        print(output)
4649        self.assertRegex(output, 'default via 192.168.5.1 proto dhcp')
4650        if main_table_is_empty:
4651            self.assertRegex(output, '192.168.5.0/24 proto dhcp')
4652        self.assertRegex(output, '192.168.5.1 proto dhcp scope link')
4653
4654        print('## dnsmasq log')
4655        self.assertTrue(search_words_in_dnsmasq_log('vendor class: SusantVendorTest', True))
4656        self.assertTrue(search_words_in_dnsmasq_log('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc'))
4657        self.assertTrue(search_words_in_dnsmasq_log('client provides name: test-hostname'))
4658        self.assertTrue(search_words_in_dnsmasq_log('26:mtu'))
4659
4660    def test_dhcp_client_settings_anonymize(self):
4661        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
4662        start_networkd()
4663        self.wait_online(['veth-peer:carrier'])
4664        start_dnsmasq()
4665        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4666
4667        self.assertFalse(search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True))
4668        self.assertFalse(search_words_in_dnsmasq_log('test-hostname'))
4669        self.assertFalse(search_words_in_dnsmasq_log('26:mtu'))
4670
4671    def test_dhcp_client_listen_port(self):
4672        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-listen-port.network')
4673        start_networkd()
4674        self.wait_online(['veth-peer:carrier'])
4675        start_dnsmasq('--dhcp-alternate-port=67,5555')
4676        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4677
4678        output = check_output('ip -4 address show dev veth99')
4679        print(output)
4680        self.assertRegex(output, '192.168.5.* dynamic')
4681
4682    def test_dhcp_client_with_static_address(self):
4683        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-v4-server-veth-peer.network',
4684                                        '25-dhcp-client-with-static-address.network')
4685        start_networkd()
4686        self.wait_online(['veth-peer:carrier'])
4687        start_dnsmasq()
4688        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4689
4690        output = check_output('ip address show dev veth99 scope global')
4691        print(output)
4692        self.assertRegex(output, r'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99')
4693        self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global secondary dynamic veth99')
4694
4695        output = check_output('ip route show dev veth99')
4696        print(output)
4697        self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
4698        self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.250')
4699        self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
4700
4701    def test_dhcp_route_table_id(self):
4702        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-v4-server-veth-peer.network', '25-dhcp-client-route-table.network')
4703        start_networkd()
4704        self.wait_online(['veth-peer:carrier'])
4705        start_dnsmasq()
4706        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4707
4708        output = check_output('ip route show table 12')
4709        print(output)
4710        self.assertRegex(output, 'veth99 proto dhcp')
4711        self.assertRegex(output, '192.168.5.1')
4712
4713    def test_dhcp_route_metric(self):
4714        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-v4-server-veth-peer.network', '25-dhcp-client-route-metric.network')
4715        start_networkd()
4716        self.wait_online(['veth-peer:carrier'])
4717        start_dnsmasq()
4718        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4719
4720        output = check_output('ip route show dev veth99')
4721        print(output)
4722        self.assertIn('default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 24', output)
4723        self.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.181 metric 24', output)
4724        self.assertIn('192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 24', output)
4725
4726    def test_dhcp_client_reassign_static_routes_ipv4(self):
4727        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
4728                                        '25-dhcp-client-reassign-static-routes-ipv4.network')
4729        start_networkd()
4730        self.wait_online(['veth-peer:carrier'])
4731        start_dnsmasq(lease_time='2m')
4732        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4733
4734        output = check_output('ip address show dev veth99 scope global')
4735        print(output)
4736        self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
4737
4738        output = check_output('ip route show dev veth99')
4739        print(output)
4740        self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
4741        self.assertRegex(output, r'192.168.5.0/24 proto static')
4742        self.assertRegex(output, r'192.168.6.0/24 proto static')
4743        self.assertRegex(output, r'192.168.7.0/24 proto static')
4744
4745        stop_dnsmasq()
4746        start_dnsmasq(ipv4_range='192.168.5.210,192.168.5.220', lease_time='2m')
4747
4748        # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
4749        print('Wait for the dynamic address to be renewed')
4750        time.sleep(125)
4751
4752        self.wait_online(['veth99:routable'])
4753
4754        output = check_output('ip route show dev veth99')
4755        print(output)
4756        self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
4757        self.assertRegex(output, r'192.168.5.0/24 proto static')
4758        self.assertRegex(output, r'192.168.6.0/24 proto static')
4759        self.assertRegex(output, r'192.168.7.0/24 proto static')
4760
4761    def test_dhcp_client_reassign_static_routes_ipv6(self):
4762        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
4763                                        '25-dhcp-client-reassign-static-routes-ipv6.network')
4764        start_networkd()
4765        self.wait_online(['veth-peer:carrier'])
4766        start_dnsmasq(lease_time='2m')
4767        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4768
4769        output = check_output('ip address show dev veth99 scope global')
4770        print(output)
4771        self.assertRegex(output, r'inet6 2600::[0-9a-f]*/128 scope global (noprefixroute dynamic|dynamic noprefixroute)')
4772
4773        output = check_output('ip -6 route show dev veth99')
4774        print(output)
4775        self.assertRegex(output, r'2600::/64 proto ra metric 1024')
4776        self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium')
4777
4778        stop_dnsmasq()
4779        start_dnsmasq(ipv6_range='2600::30,2600::40', lease_time='2m')
4780
4781        # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
4782        print('Wait for the dynamic address to be renewed')
4783        time.sleep(125)
4784
4785        self.wait_online(['veth99:routable'])
4786
4787        output = check_output('ip -6 route show dev veth99')
4788        print(output)
4789        self.assertRegex(output, r'2600::/64 proto ra metric 1024')
4790        self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium')
4791
4792    def test_dhcp_keep_configuration_dhcp(self):
4793        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-v4-server-veth-peer.network', '25-dhcp-client-keep-configuration-dhcp.network')
4794        start_networkd()
4795        self.wait_online(['veth-peer:carrier'])
4796        start_dnsmasq(lease_time='2m')
4797        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4798
4799        output = check_output('ip address show dev veth99 scope global')
4800        print(output)
4801        self.assertRegex(output, r'192.168.5.*')
4802
4803        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4804        print(output)
4805        self.assertRegex(output, r'192.168.5.*')
4806
4807        # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
4808        stop_dnsmasq()
4809
4810        # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
4811        print('Wait for the dynamic address to be expired')
4812        time.sleep(125)
4813
4814        print('The lease address should be kept after lease expired')
4815        output = check_output('ip address show dev veth99 scope global')
4816        print(output)
4817        self.assertRegex(output, r'192.168.5.*')
4818
4819        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4820        print(output)
4821        self.assertRegex(output, r'192.168.5.*')
4822
4823        check_output('systemctl stop systemd-networkd.socket')
4824        check_output('systemctl stop systemd-networkd.service')
4825
4826        print('The lease address should be kept after networkd stopped')
4827        output = check_output('ip address show dev veth99 scope global')
4828        print(output)
4829        self.assertRegex(output, r'192.168.5.*')
4830
4831        with open(os.path.join(network_unit_file_path, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a') as f:
4832            f.write('[Network]\nDHCP=no\n')
4833
4834        start_networkd()
4835        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4836
4837        print('Still the lease address should be kept after networkd restarted')
4838        output = check_output('ip address show dev veth99 scope global')
4839        print(output)
4840        self.assertRegex(output, r'192.168.5.*')
4841
4842        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
4843        print(output)
4844        self.assertRegex(output, r'192.168.5.*')
4845
4846    def test_dhcp_keep_configuration_dhcp_on_stop(self):
4847        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-v4-server-veth-peer.network', '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
4848        start_networkd()
4849        self.wait_online(['veth-peer:carrier'])
4850        start_dnsmasq(lease_time='2m')
4851        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4852
4853        output = check_output('ip address show dev veth99 scope global')
4854        print(output)
4855        self.assertRegex(output, r'192.168.5.*')
4856
4857        stop_dnsmasq()
4858        check_output('systemctl stop systemd-networkd.socket')
4859        check_output('systemctl stop systemd-networkd.service')
4860
4861        output = check_output('ip address show dev veth99 scope global')
4862        print(output)
4863        self.assertRegex(output, r'192.168.5.*')
4864
4865        restart_networkd(3)
4866        self.wait_online(['veth-peer:routable'])
4867
4868        output = check_output('ip address show dev veth99 scope global')
4869        print(output)
4870        self.assertNotRegex(output, r'192.168.5.*')
4871
4872    def test_dhcp_client_reuse_address_as_static(self):
4873        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
4874        start_networkd()
4875        self.wait_online(['veth-peer:carrier'])
4876        start_dnsmasq()
4877        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4878
4879        # link become 'routable' when at least one protocol provide an valid address.
4880        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
4881        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
4882
4883        output = check_output('ip address show dev veth99 scope global')
4884        print(output)
4885        self.assertRegex(output, '192.168.5')
4886        self.assertRegex(output, '2600::')
4887
4888        ipv4_address = re.search(r'192.168.5.[0-9]*/24', output)
4889        ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output)
4890        static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address.group(), 'Address=' + ipv6_address.group()])
4891        print(static_network)
4892
4893        remove_unit_from_networkd_path(['25-dhcp-client.network'])
4894
4895        with open(os.path.join(network_unit_file_path, '25-static.network'), mode='w') as f:
4896            f.write(static_network)
4897
4898        # When networkd started, the links are already configured, so let's wait for 5 seconds
4899        # the links to be re-configured.
4900        restart_networkd(5)
4901        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4902
4903        output = check_output('ip -4 address show dev veth99 scope global')
4904        print(output)
4905        self.assertRegex(output, '192.168.5')
4906        self.assertRegex(output, 'valid_lft forever preferred_lft forever')
4907
4908        output = check_output('ip -6 address show dev veth99 scope global')
4909        print(output)
4910        self.assertRegex(output, '2600::')
4911        self.assertRegex(output, 'valid_lft forever preferred_lft forever')
4912
4913    @expectedFailureIfModuleIsNotAvailable('vrf')
4914    def test_dhcp_client_vrf(self):
4915        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
4916                                        '25-vrf.netdev', '25-vrf.network')
4917        start_networkd()
4918        self.wait_online(['veth-peer:carrier'])
4919        start_dnsmasq()
4920        self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
4921
4922        # link become 'routable' when at least one protocol provide an valid address.
4923        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
4924        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
4925
4926        print('## ip -d link show dev vrf99')
4927        output = check_output('ip -d link show dev vrf99')
4928        print(output)
4929        self.assertRegex(output, 'vrf table 42')
4930
4931        print('## ip address show vrf vrf99')
4932        output = check_output('ip address show vrf vrf99')
4933        print(output)
4934        self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
4935        self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
4936        self.assertRegex(output, 'inet6 .* scope link')
4937
4938        print('## ip address show dev veth99')
4939        output = check_output('ip address show dev veth99')
4940        print(output)
4941        self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
4942        self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
4943        self.assertRegex(output, 'inet6 .* scope link')
4944
4945        print('## ip route show vrf vrf99')
4946        output = check_output('ip route show vrf vrf99')
4947        print(output)
4948        self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
4949        self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
4950        self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
4951
4952        print('## ip route show table main dev veth99')
4953        output = check_output('ip route show table main dev veth99')
4954        print(output)
4955        self.assertEqual(output, '')
4956
4957    def test_dhcp_client_gateway_ipv4(self):
4958        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
4959                                        '25-dhcp-client-gateway-ipv4.network')
4960        start_networkd()
4961        self.wait_online(['veth-peer:carrier'])
4962        start_dnsmasq()
4963        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4964
4965        output = check_output('ip route list dev veth99')
4966        print(output)
4967        self.assertRegex(output, 'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]*')
4968        self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
4969
4970        with open(os.path.join(network_unit_file_path, '25-dhcp-client-gateway-ipv4.network'), mode='a') as f:
4971            f.write('[DHCPv4]\nUseGateway=no\n')
4972
4973        rc = call(*networkctl_cmd, 'reload', env=env)
4974        self.assertEqual(rc, 0)
4975
4976        time.sleep(2)
4977        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4978
4979        output = check_output('ip route list dev veth99')
4980        print(output)
4981        self.assertNotRegex(output, 'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]*')
4982        self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
4983
4984    def test_dhcp_client_gateway_ipv6(self):
4985        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
4986                                        '25-dhcp-client-gateway-ipv6.network')
4987        start_networkd()
4988        self.wait_online(['veth-peer:carrier'])
4989        start_dnsmasq()
4990        self.wait_online(['veth99:routable', 'veth-peer:routable'])
4991
4992        output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
4993        print(output)
4994        self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
4995
4996    def test_dhcp_client_gateway_onlink_implicit(self):
4997        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
4998                                        '25-dhcp-client-gateway-onlink-implicit.network')
4999        start_networkd()
5000        self.wait_online(['veth-peer:carrier'])
5001        start_dnsmasq()
5002        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5003
5004        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
5005        print(output)
5006        self.assertRegex(output, '192.168.5')
5007
5008        output = check_output('ip route list dev veth99 10.0.0.0/8')
5009        print(output)
5010        self.assertRegex(output, 'onlink')
5011        output = check_output('ip route list dev veth99 192.168.100.0/24')
5012        print(output)
5013        self.assertRegex(output, 'onlink')
5014
5015    def test_dhcp_client_with_ipv4ll_with_dhcp_server(self):
5016        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
5017                                        '25-dhcp-client-with-ipv4ll.network')
5018        start_networkd()
5019        self.wait_online(['veth-peer:carrier'])
5020        start_dnsmasq(lease_time='2m')
5021        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5022
5023        output = check_output('ip address show dev veth99')
5024        print(output)
5025
5026        output = check_output('ip -6 address show dev veth99 scope global dynamic')
5027        self.assertNotRegex(output, r'inet6 2600::[0-9a-f]+/128 scope global dynamic')
5028        output = check_output('ip -6 address show dev veth99 scope link')
5029        self.assertRegex(output, r'inet6 .* scope link')
5030        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5031        self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
5032        output = check_output('ip -4 address show dev veth99 scope link')
5033        self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
5034
5035        print('Wait for the dynamic address to be expired')
5036        time.sleep(130)
5037
5038        output = check_output('ip address show dev veth99')
5039        print(output)
5040
5041        output = check_output('ip -6 address show dev veth99 scope global dynamic')
5042        self.assertNotRegex(output, r'inet6 2600::[0-9a-f]+/128 scope global dynamic')
5043        output = check_output('ip -6 address show dev veth99 scope link')
5044        self.assertRegex(output, r'inet6 .* scope link')
5045        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5046        self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
5047        output = check_output('ip -4 address show dev veth99 scope link')
5048        self.assertNotRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
5049
5050        search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
5051
5052    def test_dhcp_client_with_ipv4ll_without_dhcp_server(self):
5053        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
5054                                        '25-dhcp-client-with-ipv4ll.network')
5055        start_networkd()
5056        # we need to increase timeout above default, as this will need to wait for
5057        # systemd-networkd to get the dhcpv4 transient failure event
5058        self.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout='60s')
5059
5060        output = check_output('ip address show dev veth99')
5061        print(output)
5062
5063        output = check_output('ip -6 address show dev veth99 scope global dynamic')
5064        self.assertNotRegex(output, r'inet6 2600::[0-9a-f]+/128 scope global dynamic')
5065        output = check_output('ip -6 address show dev veth99 scope link')
5066        self.assertRegex(output, r'inet6 .* scope link')
5067        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5068        self.assertNotRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
5069        output = check_output('ip -4 address show dev veth99 scope link')
5070        self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
5071
5072        start_dnsmasq(lease_time='2m')
5073        self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
5074        self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.255\.255\.255 scope link', scope='link', ipv='-4')
5075
5076    def test_dhcp_client_route_remove_on_renew(self):
5077        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network',
5078                                        '25-dhcp-client-ipv4-only-ipv6-disabled.network')
5079        start_networkd()
5080        self.wait_online(['veth-peer:carrier'])
5081        start_dnsmasq(ipv4_range='192.168.5.100,192.168.5.199', lease_time='2m')
5082        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5083
5084        # test for issue #12490
5085
5086        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5087        print(output)
5088        self.assertRegex(output, 'inet 192.168.5.1[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5089        address1=None
5090        for line in output.splitlines():
5091            if 'brd 192.168.5.255 scope global dynamic veth99' in line:
5092                address1 = line.split()[1].split('/')[0]
5093                break
5094
5095        output = check_output('ip -4 route show dev veth99')
5096        print(output)
5097        self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
5098        self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
5099
5100        stop_dnsmasq()
5101        start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m')
5102
5103        print('Wait for the dynamic address to be expired')
5104        time.sleep(130)
5105
5106        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5107        print(output)
5108        self.assertRegex(output, 'inet 192.168.5.2[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5109        address2=None
5110        for line in output.splitlines():
5111            if 'metric 1024 brd 192.168.5.255 scope global dynamic veth99' in line:
5112                address2 = line.split()[1].split('/')[0]
5113                break
5114
5115        self.assertNotEqual(address1, address2)
5116
5117        output = check_output('ip -4 route show dev veth99')
5118        print(output)
5119        self.assertNotRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
5120        self.assertNotRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
5121        self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 1024')
5122        self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 1024')
5123
5124    def test_dhcp_client_use_dns_yes(self):
5125        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-use-dns-yes.network')
5126
5127        start_networkd()
5128        self.wait_online(['veth-peer:carrier'])
5129        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
5130        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5131
5132        # link become 'routable' when at least one protocol provide an valid address.
5133        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
5134        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
5135
5136        time.sleep(3)
5137        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
5138        print(output)
5139        self.assertRegex(output, '192.168.5.1')
5140        self.assertRegex(output, '2600::1')
5141
5142        # TODO: check json string
5143        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
5144
5145    def test_dhcp_client_use_dns_no(self):
5146        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-use-dns-no.network')
5147
5148        start_networkd()
5149        self.wait_online(['veth-peer:carrier'])
5150        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
5151        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5152
5153        # link become 'routable' when at least one protocol provide an valid address.
5154        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
5155        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
5156
5157        time.sleep(3)
5158        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
5159        print(output)
5160        self.assertNotRegex(output, '192.168.5.1')
5161        self.assertNotRegex(output, '2600::1')
5162
5163    def test_dhcp_client_use_dns_ipv4(self):
5164        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-use-dns-ipv4.network')
5165
5166        start_networkd()
5167        self.wait_online(['veth-peer:carrier'])
5168        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
5169        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5170
5171        # link become 'routable' when at least one protocol provide an valid address.
5172        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
5173        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
5174
5175        time.sleep(3)
5176        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
5177        print(output)
5178        self.assertRegex(output, '192.168.5.1')
5179        self.assertNotRegex(output, '2600::1')
5180
5181    def test_dhcp_client_use_dns_ipv4_and_ra(self):
5182        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-use-dns-ipv4-and-ra.network')
5183
5184        start_networkd()
5185        self.wait_online(['veth-peer:carrier'])
5186        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
5187        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5188
5189        # link become 'routable' when at least one protocol provide an valid address.
5190        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
5191        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
5192
5193        time.sleep(3)
5194        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
5195        print(output)
5196        self.assertRegex(output, '192.168.5.1')
5197        self.assertRegex(output, '2600::1')
5198
5199    def test_dhcp_client_use_domains(self):
5200        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-use-domains.network')
5201
5202        start_networkd()
5203        self.wait_online(['veth-peer:carrier'])
5204        start_dnsmasq('--dhcp-option=option:domain-search,example.com')
5205        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5206
5207        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
5208        print(output)
5209        self.assertRegex(output, 'Search Domains: example.com')
5210
5211        time.sleep(3)
5212        output = check_output(*resolvectl_cmd, 'domain', 'veth99', env=env)
5213        print(output)
5214        self.assertRegex(output, 'example.com')
5215
5216    def test_dhcp_client_decline(self):
5217        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-decline.network', '25-dhcp-client-decline.network')
5218
5219        start_networkd()
5220        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5221
5222        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5223        print(output)
5224        self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5225
5226    def test_dhcp_client_allow_list(self):
5227        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp-server-decline.network', '25-dhcp-client-allow-list.network')
5228
5229        start_networkd()
5230        self.wait_online(['veth99:routable', 'veth-peer:routable'])
5231
5232        output = check_output('ip -4 address show dev veth99 scope global dynamic')
5233        print(output)
5234        self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
5235
5236class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
5237    links = [
5238        'dummy97',
5239        'dummy98',
5240        'dummy99',
5241        'test1',
5242        'veth97',
5243        'veth98',
5244        'veth99',
5245    ]
5246
5247    units = [
5248        '11-dummy.netdev',
5249        '12-dummy.netdev',
5250        '13-dummy.netdev',
5251        '25-veth.netdev',
5252        '25-veth-downstream-veth97.netdev',
5253        '25-veth-downstream-veth98.netdev',
5254        '80-6rd-tunnel.network',
5255        '25-dhcp-pd-downstream-dummy97.network',
5256        '25-dhcp-pd-downstream-dummy98.network',
5257        '25-dhcp-pd-downstream-dummy99.network',
5258        '25-dhcp-pd-downstream-test1.network',
5259        '25-dhcp-pd-downstream-veth97.network',
5260        '25-dhcp-pd-downstream-veth97-peer.network',
5261        '25-dhcp-pd-downstream-veth98.network',
5262        '25-dhcp-pd-downstream-veth98-peer.network',
5263        '25-dhcp4-6rd-server.network',
5264        '25-dhcp4-6rd-upstream.network',
5265        '25-dhcp6pd-server.network',
5266        '25-dhcp6pd-upstream.network',
5267    ]
5268
5269    def setUp(self):
5270        stop_isc_dhcpd()
5271        stop_dnsmasq()
5272        remove_links(self.links)
5273        stop_networkd(show_logs=False)
5274
5275    def tearDown(self):
5276        stop_isc_dhcpd()
5277        stop_dnsmasq()
5278        remove_links(self.links)
5279        remove_unit_from_networkd_path(self.units)
5280        stop_networkd(show_logs=True)
5281
5282    def test_dhcp6pd(self):
5283        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
5284                                        '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
5285                                        '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
5286                                        '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
5287                                        '25-dhcp-pd-downstream-dummy97.network',
5288                                        '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
5289                                        '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
5290
5291        start_networkd()
5292        self.wait_online(['veth-peer:routable'])
5293        start_isc_dhcpd('veth-peer', 'isc-dhcpd-dhcp6pd.conf', ip='-6')
5294        self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
5295                          'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
5296
5297        print('### ip -6 address show dev veth-peer scope global')
5298        output = check_output('ip -6 address show dev veth-peer scope global')
5299        print(output)
5300        self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
5301
5302        # Link     Subnet IDs
5303        # test1:   0x00
5304        # dummy97: 0x01 (The link will appear later)
5305        # dummy98: 0x00
5306        # dummy99: auto -> 0x02 (No address assignment)
5307        # veth97:  0x08
5308        # veth98:  0x09
5309        # veth99:  0x10
5310
5311        print('### ip -6 address show dev veth99 scope global')
5312        output = check_output('ip -6 address show dev veth99 scope global')
5313        print(output)
5314        # IA_NA
5315        self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
5316        # address in IA_PD (Token=static)
5317        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
5318        # address in IA_PD (Token=eui64)
5319        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
5320        # address in IA_PD (temporary)
5321        # Note that the temporary addresses may appear after the link enters configured state
5322        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')
5323
5324        print('### ip -6 address show dev test1 scope global')
5325        output = check_output('ip -6 address show dev test1 scope global')
5326        print(output)
5327        # address in IA_PD (Token=static)
5328        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5329        # address in IA_PD (temporary)
5330        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')
5331
5332        print('### ip -6 address show dev dummy98 scope global')
5333        output = check_output('ip -6 address show dev dummy98 scope global')
5334        print(output)
5335        # address in IA_PD (Token=static)
5336        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5337        # address in IA_PD (temporary)
5338        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')
5339
5340        print('### ip -6 address show dev dummy99 scope global')
5341        output = check_output('ip -6 address show dev dummy99 scope global')
5342        print(output)
5343        # Assign=no
5344        self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
5345
5346        print('### ip -6 address show dev veth97 scope global')
5347        output = check_output('ip -6 address show dev veth97 scope global')
5348        print(output)
5349        # address in IA_PD (Token=static)
5350        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5351        # address in IA_PD (Token=eui64)
5352        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
5353        # address in IA_PD (temporary)
5354        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')
5355
5356        print('### ip -6 address show dev veth97-peer scope global')
5357        output = check_output('ip -6 address show dev veth97-peer scope global')
5358        print(output)
5359        # NDisc address (Token=static)
5360        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
5361        # NDisc address (Token=eui64)
5362        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
5363        # NDisc address (temporary)
5364        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')
5365
5366        print('### ip -6 address show dev veth98 scope global')
5367        output = check_output('ip -6 address show dev veth98 scope global')
5368        print(output)
5369        # address in IA_PD (Token=static)
5370        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5371        # address in IA_PD (Token=eui64)
5372        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
5373        # address in IA_PD (temporary)
5374        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')
5375
5376        print('### ip -6 address show dev veth98-peer scope global')
5377        output = check_output('ip -6 address show dev veth98-peer scope global')
5378        print(output)
5379        # NDisc address (Token=static)
5380        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
5381        # NDisc address (Token=eui64)
5382        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
5383        # NDisc address (temporary)
5384        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')
5385
5386        print('### ip -6 route show type unreachable')
5387        output = check_output('ip -6 route show type unreachable')
5388        print(output)
5389        self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
5390
5391        print('### ip -6 route show dev veth99')
5392        output = check_output('ip -6 route show dev veth99')
5393        print(output)
5394        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
5395
5396        print('### ip -6 route show dev test1')
5397        output = check_output('ip -6 route show dev test1')
5398        print(output)
5399        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
5400
5401        print('### ip -6 route show dev dummy98')
5402        output = check_output('ip -6 route show dev dummy98')
5403        print(output)
5404        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
5405
5406        print('### ip -6 route show dev dummy99')
5407        output = check_output('ip -6 route show dev dummy99')
5408        print(output)
5409        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
5410
5411        print('### ip -6 route show dev veth97')
5412        output = check_output('ip -6 route show dev veth97')
5413        print(output)
5414        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
5415
5416        print('### ip -6 route show dev veth97-peer')
5417        output = check_output('ip -6 route show dev veth97-peer')
5418        print(output)
5419        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
5420
5421        print('### ip -6 route show dev veth98')
5422        output = check_output('ip -6 route show dev veth98')
5423        print(output)
5424        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
5425
5426        print('### ip -6 route show dev veth98-peer')
5427        output = check_output('ip -6 route show dev veth98-peer')
5428        print(output)
5429        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
5430
5431        # Test case for a downstream which appears later
5432        check_output('ip link add dummy97 type dummy')
5433        self.wait_online(['dummy97:routable'])
5434
5435        print('### ip -6 address show dev dummy97 scope global')
5436        output = check_output('ip -6 address show dev dummy97 scope global')
5437        print(output)
5438        # address in IA_PD (Token=static)
5439        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5440        # address in IA_PD (temporary)
5441        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')
5442
5443        print('### ip -6 route show dev dummy97')
5444        output = check_output('ip -6 route show dev dummy97')
5445        print(output)
5446        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
5447
5448        # Test case for reconfigure
5449        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', 'dummy99', env=env)
5450        self.wait_online(['dummy98:routable'])
5451
5452        print('### ip -6 address show dev dummy98 scope global')
5453        output = check_output('ip -6 address show dev dummy98 scope global')
5454        print(output)
5455        # address in IA_PD (Token=static)
5456        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5457        # address in IA_PD (temporary)
5458        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')
5459
5460        print('### ip -6 address show dev dummy99 scope global')
5461        output = check_output('ip -6 address show dev dummy99 scope global')
5462        print(output)
5463        # Assign=no
5464        self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02')
5465
5466        print('### ip -6 route show dev dummy98')
5467        output = check_output('ip -6 route show dev dummy98')
5468        print(output)
5469        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
5470
5471        print('### ip -6 route show dev dummy99')
5472        output = check_output('ip -6 route show dev dummy99')
5473        print(output)
5474        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
5475
5476    def verify_dhcp4_6rd(self, tunnel_name):
5477        print('### ip -4 address show dev veth-peer scope global')
5478        output = check_output('ip -4 address show dev veth-peer scope global')
5479        print(output)
5480        self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output)
5481
5482        # Link     Subnet IDs
5483        # test1:   0x00
5484        # dummy97: 0x01 (The link will appear later)
5485        # dummy98: 0x00
5486        # dummy99: auto -> 0x0[23] (No address assignment)
5487        # 6rd-XXX: auto -> 0x0[23]
5488        # veth97:  0x08
5489        # veth98:  0x09
5490        # veth99:  0x10
5491
5492        print('### ip -4 address show dev veth99 scope global')
5493        output = check_output('ip -4 address show dev veth99 scope global')
5494        print(output)
5495        self.assertRegex(output, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
5496
5497        print('### ip -6 address show dev veth99 scope global')
5498        output = check_output('ip -6 address show dev veth99 scope global')
5499        print(output)
5500        # address in IA_PD (Token=static)
5501        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5502        # address in IA_PD (Token=eui64)
5503        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
5504        # address in IA_PD (temporary)
5505        # Note that the temporary addresses may appear after the link enters configured state
5506        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')
5507
5508        print('### ip -6 address show dev test1 scope global')
5509        output = check_output('ip -6 address show dev test1 scope global')
5510        print(output)
5511        # address in IA_PD (Token=static)
5512        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5513        # address in IA_PD (temporary)
5514        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')
5515
5516        print('### ip -6 address show dev dummy98 scope global')
5517        output = check_output('ip -6 address show dev dummy98 scope global')
5518        print(output)
5519        # address in IA_PD (Token=static)
5520        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5521        # address in IA_PD (temporary)
5522        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')
5523
5524        print('### ip -6 address show dev dummy99 scope global')
5525        output = check_output('ip -6 address show dev dummy99 scope global')
5526        print(output)
5527        # Assign=no
5528        self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
5529
5530        print('### ip -6 address show dev veth97 scope global')
5531        output = check_output('ip -6 address show dev veth97 scope global')
5532        print(output)
5533        # address in IA_PD (Token=static)
5534        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5535        # address in IA_PD (Token=eui64)
5536        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
5537        # address in IA_PD (temporary)
5538        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')
5539
5540        print('### ip -6 address show dev veth97-peer scope global')
5541        output = check_output('ip -6 address show dev veth97-peer scope global')
5542        print(output)
5543        # NDisc address (Token=static)
5544        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
5545        # NDisc address (Token=eui64)
5546        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
5547        # NDisc address (temporary)
5548        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')
5549
5550        print('### ip -6 address show dev veth98 scope global')
5551        output = check_output('ip -6 address show dev veth98 scope global')
5552        print(output)
5553        # address in IA_PD (Token=static)
5554        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5555        # address in IA_PD (Token=eui64)
5556        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
5557        # address in IA_PD (temporary)
5558        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')
5559
5560        print('### ip -6 address show dev veth98-peer scope global')
5561        output = check_output('ip -6 address show dev veth98-peer scope global')
5562        print(output)
5563        # NDisc address (Token=static)
5564        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
5565        # NDisc address (Token=eui64)
5566        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
5567        # NDisc address (temporary)
5568        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')
5569
5570        print('### ip -6 route show type unreachable')
5571        output = check_output('ip -6 route show type unreachable')
5572        print(output)
5573        self.assertRegex(output, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
5574
5575        print('### ip -6 route show dev veth99')
5576        output = check_output('ip -6 route show dev veth99')
5577        print(output)
5578        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
5579
5580        print('### ip -6 route show dev test1')
5581        output = check_output('ip -6 route show dev test1')
5582        print(output)
5583        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
5584
5585        print('### ip -6 route show dev dummy98')
5586        output = check_output('ip -6 route show dev dummy98')
5587        print(output)
5588        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
5589
5590        print('### ip -6 route show dev dummy99')
5591        output = check_output('ip -6 route show dev dummy99')
5592        print(output)
5593        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
5594
5595        print('### ip -6 route show dev veth97')
5596        output = check_output('ip -6 route show dev veth97')
5597        print(output)
5598        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
5599
5600        print('### ip -6 route show dev veth97-peer')
5601        output = check_output('ip -6 route show dev veth97-peer')
5602        print(output)
5603        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
5604
5605        print('### ip -6 route show dev veth98')
5606        output = check_output('ip -6 route show dev veth98')
5607        print(output)
5608        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
5609
5610        print('### ip -6 route show dev veth98-peer')
5611        output = check_output('ip -6 route show dev veth98-peer')
5612        print(output)
5613        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
5614
5615        print('### ip -6 address show dev dummy97 scope global')
5616        output = check_output('ip -6 address show dev dummy97 scope global')
5617        print(output)
5618        # address in IA_PD (Token=static)
5619        self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
5620        # address in IA_PD (temporary)
5621        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')
5622
5623        print('### ip -6 route show dev dummy97')
5624        output = check_output('ip -6 route show dev dummy97')
5625        print(output)
5626        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
5627
5628        print('### ip -d link show dev {}'.format(tunnel_name))
5629        output = check_output('ip -d link show dev {}'.format(tunnel_name))
5630        print(output)
5631        self.assertIn('link/sit 10.100.100.', output)
5632        self.assertIn('local 10.100.100.', output)
5633        self.assertIn('ttl 64', output)
5634        self.assertIn('6rd-prefix 2001:db8::/32', output)
5635        self.assertIn('6rd-relay_prefix 10.0.0.0/8', output)
5636
5637        print('### ip -6 address show dev {}'.format(tunnel_name))
5638        output = check_output('ip -6 address show dev {}'.format(tunnel_name))
5639        print(output)
5640        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')
5641        self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global')
5642
5643        print('### ip -6 route show dev {}'.format(tunnel_name))
5644        output = check_output('ip -6 route show dev {}'.format(tunnel_name))
5645        print(output)
5646        self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
5647        self.assertRegex(output, '::/96 proto kernel metric [0-9]*')
5648
5649        print('### ip -6 route show default')
5650        output = check_output('ip -6 route show default')
5651        print(output)
5652        self.assertIn('default', output)
5653        self.assertIn('via ::10.0.0.1 dev {}'.format(tunnel_name), output)
5654
5655    def test_dhcp4_6rd(self):
5656        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
5657                                        '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
5658                                        '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
5659                                        '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
5660                                        '25-dhcp-pd-downstream-dummy97.network',
5661                                        '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
5662                                        '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
5663                                        '80-6rd-tunnel.network')
5664
5665        start_networkd()
5666        self.wait_online(['veth-peer:routable'])
5667
5668        # ipv4masklen: 8
5669        # 6rd-prefix: 2001:db8::/32
5670        # br-addresss: 10.0.0.1
5671
5672        start_dnsmasq(additional_options='--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', ipv4_range='10.100.100.100,10.100.100.200', ipv4_router='10.0.0.1', lease_time='2m')
5673        self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
5674                          'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
5675
5676        # Test case for a downstream which appears later
5677        check_output('ip link add dummy97 type dummy')
5678        self.wait_online(['dummy97:routable'])
5679
5680        # Find tunnel name
5681        tunnel_name = None
5682        for name in os.listdir('/sys/class/net/'):
5683            if name.startswith('6rd-'):
5684                tunnel_name = name
5685                break
5686
5687        self.wait_online(['{}:routable'.format(tunnel_name)])
5688
5689        self.verify_dhcp4_6rd(tunnel_name)
5690
5691        # Test case for reconfigure
5692        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', 'dummy99', env=env)
5693        self.wait_online(['dummy98:routable', 'dummy99:degraded'])
5694
5695        self.verify_dhcp4_6rd(tunnel_name)
5696
5697        # Test for renewing/rebinding lease
5698        print('wait for 120 sec')
5699        time.sleep(30)
5700        print('wait for  90 sec')
5701        time.sleep(30)
5702        print('wait for  60 sec')
5703        time.sleep(30)
5704        print('wait for  30 sec')
5705        time.sleep(30)
5706
5707        dump_dnsmasq_log_file()
5708
5709        self.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
5710                          'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
5711
5712        self.verify_dhcp4_6rd(tunnel_name)
5713
5714class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
5715    links = [
5716        'dummy98',
5717        'veth99',
5718    ]
5719
5720    units = [
5721        '12-dummy.netdev',
5722        '25-veth.netdev',
5723        '25-ipv6ra-prefix-client-deny-list.network',
5724        '25-ipv6ra-prefix-client.network',
5725        '25-ipv6ra-prefix.network',
5726        '25-ipv6ra-uplink.network',
5727    ]
5728
5729    def setUp(self):
5730        remove_links(self.links)
5731        stop_networkd(show_logs=False)
5732
5733    def tearDown(self):
5734        remove_links(self.links)
5735        remove_unit_from_networkd_path(self.units)
5736        stop_networkd(show_logs=True)
5737
5738    def test_ipv6_route_prefix(self):
5739        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
5740                                        '12-dummy.netdev', '25-ipv6ra-uplink.network')
5741
5742        start_networkd()
5743        self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
5744
5745        output = check_output('ip address show dev veth-peer')
5746        print(output)
5747        self.assertIn('inet6 2001:db8:0:1:', output)
5748        self.assertNotIn('inet6 2001:db8:0:2:', output)
5749        self.assertNotIn('inet6 2001:db8:0:3:', output)
5750
5751        output = check_output('ip -6 route show dev veth-peer')
5752        print(output)
5753        self.assertIn('2001:db8:0:1::/64 proto ra', output)
5754        self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
5755        self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
5756        self.assertIn('2001:db0:fff::/64 via ', output)
5757        self.assertNotIn('2001:db1:fff::/64 via ', output)
5758        self.assertNotIn('2001:db2:fff::/64 via ', output)
5759
5760        output = check_output('ip address show dev veth99')
5761        print(output)
5762        self.assertNotIn('inet6 2001:db8:0:1:', output)
5763        self.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output)
5764        self.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output)
5765        self.assertNotIn('inet6 2001:db8:0:3:', output)
5766
5767        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
5768        print(output)
5769        self.assertRegex(output, '2001:db8:1:1::2')
5770
5771        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
5772        print(output)
5773        self.assertIn('example.com', output)
5774
5775        # TODO: check json string
5776        check_output(*networkctl_cmd, '--json=short', 'status', env=env)
5777
5778    def test_ipv6_route_prefix_deny_list(self):
5779        copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
5780                                        '12-dummy.netdev', '25-ipv6ra-uplink.network')
5781
5782        start_networkd()
5783        self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
5784
5785        output = check_output('ip address show dev veth-peer')
5786        print(output)
5787        self.assertIn('inet6 2001:db8:0:1:', output)
5788        self.assertNotIn('inet6 2001:db8:0:2:', output)
5789
5790        output = check_output('ip -6 route show dev veth-peer')
5791        print(output)
5792        self.assertIn('2001:db8:0:1::/64 proto ra', output)
5793        self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
5794        self.assertIn('2001:db0:fff::/64 via ', output)
5795        self.assertNotIn('2001:db1:fff::/64 via ', output)
5796
5797        output = check_output('ip address show dev veth99')
5798        print(output)
5799        self.assertNotIn('inet6 2001:db8:0:1:', output)
5800        self.assertIn('inet6 2001:db8:0:2:', output)
5801
5802        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
5803        print(output)
5804        self.assertRegex(output, '2001:db8:1:1::2')
5805
5806        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
5807        print(output)
5808        self.assertIn('example.com', output)
5809
5810class NetworkdMTUTests(unittest.TestCase, Utilities):
5811    links = ['dummy98']
5812
5813    units = [
5814        '12-dummy.netdev',
5815        '12-dummy-mtu.netdev',
5816        '12-dummy-mtu.link',
5817        '12-dummy.network',
5818        ]
5819
5820    def setUp(self):
5821        remove_links(self.links)
5822        stop_networkd(show_logs=False)
5823
5824    def tearDown(self):
5825        remove_links(self.links)
5826        remove_unit_from_networkd_path(self.units)
5827        stop_networkd(show_logs=True)
5828
5829    def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
5830        if not ipv6_mtu:
5831            ipv6_mtu = mtu
5832
5833        # test normal start
5834        start_networkd()
5835        self.wait_online(['dummy98:routable'])
5836        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu)
5837        self.assertEqual(read_link_attr('dummy98', 'mtu'), mtu)
5838
5839        # test normal restart
5840        restart_networkd()
5841        self.wait_online(['dummy98:routable'])
5842        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu)
5843        self.assertEqual(read_link_attr('dummy98', 'mtu'), mtu)
5844
5845        if reset:
5846            self.reset_check_mtu(mtu, ipv6_mtu)
5847
5848    def reset_check_mtu(self, mtu, ipv6_mtu=None):
5849        ''' test setting mtu/ipv6_mtu with interface already up '''
5850        stop_networkd()
5851
5852        # note - changing the device mtu resets the ipv6 mtu
5853        run('ip link set up mtu 1501 dev dummy98')
5854        run('ip link set up mtu 1500 dev dummy98')
5855        self.assertEqual(read_link_attr('dummy98', 'mtu'), '1500')
5856        self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), '1500')
5857
5858        self.check_mtu(mtu, ipv6_mtu, reset=False)
5859
5860    def test_mtu_network(self):
5861        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
5862        self.check_mtu('1600')
5863
5864    def test_mtu_netdev(self):
5865        copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network', dropins=False)
5866        # note - MTU set by .netdev happens ONLY at device creation!
5867        self.check_mtu('1600', reset=False)
5868
5869    def test_mtu_link(self):
5870        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', dropins=False)
5871        # must reload udev because it only picks up new files after 3 second delay
5872        call('udevadm control --reload')
5873        # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
5874        self.check_mtu('1600', reset=False)
5875
5876    def test_ipv6_mtu(self):
5877        ''' set ipv6 mtu without setting device mtu '''
5878        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
5879        self.check_mtu('1500', '1400')
5880
5881    def test_ipv6_mtu_toolarge(self):
5882        ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
5883        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
5884        self.check_mtu('1500', '1500')
5885
5886    def test_mtu_network_ipv6_mtu(self):
5887        ''' set ipv6 mtu and set device mtu via network file '''
5888        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
5889        self.check_mtu('1600', '1550')
5890
5891    def test_mtu_netdev_ipv6_mtu(self):
5892        ''' set ipv6 mtu and set device mtu via netdev file '''
5893        copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
5894        self.check_mtu('1600', '1550', reset=False)
5895
5896    def test_mtu_link_ipv6_mtu(self):
5897        ''' set ipv6 mtu and set device mtu via link file '''
5898        copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
5899        # must reload udev because it only picks up new files after 3 second delay
5900        call('udevadm control --reload')
5901        self.check_mtu('1600', '1550', reset=False)
5902
5903
5904if __name__ == '__main__':
5905    parser = argparse.ArgumentParser()
5906    parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
5907    parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
5908    parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
5909    parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
5910    parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
5911    parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
5912    parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
5913    parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
5914    parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
5915    parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
5916    parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
5917    parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
5918    parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
5919    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)
5920    ns, unknown_args = parser.parse_known_args(namespace=unittest)
5921
5922    if ns.build_dir:
5923        if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
5924            print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
5925        networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
5926        resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
5927        udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
5928        wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
5929        networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
5930        resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
5931        timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
5932    else:
5933        if ns.networkd_bin:
5934            networkd_bin = ns.networkd_bin
5935        if ns.resolved_bin:
5936            resolved_bin = ns.resolved_bin
5937        if ns.udevd_bin:
5938            udevd_bin = ns.udevd_bin
5939        if ns.wait_online_bin:
5940            wait_online_bin = ns.wait_online_bin
5941        if ns.networkctl_bin:
5942            networkctl_bin = ns.networkctl_bin
5943        if ns.resolvectl_bin:
5944            resolvectl_bin = ns.resolvectl_bin
5945        if ns.timedatectl_bin:
5946            timedatectl_bin = ns.timedatectl_bin
5947
5948    use_valgrind = ns.use_valgrind
5949    enable_debug = ns.enable_debug
5950    asan_options = ns.asan_options
5951    lsan_options = ns.lsan_options
5952    ubsan_options = ns.ubsan_options
5953    with_coverage = ns.with_coverage
5954
5955    if use_valgrind:
5956        networkctl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin]
5957        resolvectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin]
5958        timedatectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin]
5959        wait_online_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin]
5960    else:
5961        networkctl_cmd = [networkctl_bin]
5962        resolvectl_cmd = [resolvectl_bin]
5963        timedatectl_cmd = [timedatectl_bin]
5964        wait_online_cmd = [wait_online_bin]
5965
5966    if enable_debug:
5967        env.update({ 'SYSTEMD_LOG_LEVEL' : 'debug' })
5968    if asan_options:
5969        env.update({ 'ASAN_OPTIONS' : asan_options })
5970    if lsan_options:
5971        env.update({ 'LSAN_OPTIONS' : lsan_options })
5972    if ubsan_options:
5973        env.update({ 'UBSAN_OPTIONS' : ubsan_options })
5974
5975    sys.argv[1:] = unknown_args
5976    unittest.main(verbosity=3)
5977