2 # SPDX-License-Identifier: LGPL-2.1+
3 # systemd-networkd tests
15 from shutil
import copytree
17 network_unit_file_path
='/run/systemd/network'
18 networkd_runtime_directory
='/run/systemd/netif'
19 networkd_ci_path
='/run/networkd-ci'
20 network_sysctl_ipv6_path
='/proc/sys/net/ipv6/conf'
21 network_sysctl_ipv4_path
='/proc/sys/net/ipv4/conf'
23 dnsmasq_pid_file
='/run/networkd-ci/test-test-dnsmasq.pid'
24 dnsmasq_log_file
='/run/networkd-ci/test-dnsmasq-log-file'
26 systemd_lib_paths
=['/usr/lib/systemd', '/lib/systemd']
27 which_paths
=':'.join(systemd_lib_paths
+ os
.getenv('PATH', os
.defpath
).lstrip(':').split(':'))
29 networkd_bin
=shutil
.which('systemd-networkd', path
=which_paths
)
30 resolved_bin
=shutil
.which('systemd-resolved', path
=which_paths
)
31 udevd_bin
=shutil
.which('systemd-udevd', path
=which_paths
)
32 wait_online_bin
=shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
33 networkctl_bin
=shutil
.which('networkctl', path
=which_paths
)
34 resolvectl_bin
=shutil
.which('resolvectl', path
=which_paths
)
35 timedatectl_bin
=shutil
.which('timedatectl', path
=which_paths
)
46 def check_output(*command
, **kwargs
):
47 # This replaces both check_output and check_call (output can be ignored)
48 command
= command
[0].split() + list(command
[1:])
49 return subprocess
.check_output(command
, universal_newlines
=True, **kwargs
).rstrip()
51 def call(*command
, **kwargs
):
52 command
= command
[0].split() + list(command
[1:])
53 return subprocess
.call(command
, universal_newlines
=True, **kwargs
)
55 def run(*command
, **kwargs
):
56 command
= command
[0].split() + list(command
[1:])
57 return subprocess
.run(command
, universal_newlines
=True, **kwargs
)
59 def is_module_available(module_name
):
60 lsmod_output
= check_output('lsmod')
61 module_re
= re
.compile(rf
'^{re.escape(module_name)}\b', re
.MULTILINE
)
62 return module_re
.search(lsmod_output
) or not call('modprobe', module_name
, stderr
=subprocess
.DEVNULL
)
64 def expectedFailureIfModuleIsNotAvailable(module_name
):
66 if not is_module_available(module_name
):
67 return unittest
.expectedFailure(func
)
72 def expectedFailureIfERSPANModuleIsNotAvailable():
74 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
)
76 call('ip link del erspan99')
79 return unittest
.expectedFailure(func
)
83 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
85 rc
= call('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7', stderr
=subprocess
.DEVNULL
)
87 call('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
90 return unittest
.expectedFailure(func
)
94 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
96 rc
= call('ip rule add not from 192.168.100.19 ipproto tcp table 7', stderr
=subprocess
.DEVNULL
)
98 call('ip rule del not from 192.168.100.19 ipproto tcp table 7')
101 return unittest
.expectedFailure(func
)
105 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
108 rc
= call('ip rule add from 192.168.100.19 table 7 uidrange 200-300', stderr
=subprocess
.DEVNULL
)
110 ret
= run('ip rule list from 192.168.100.19 table 7', stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
)
111 if ret
.returncode
== 0 and 'uidrange 200-300' in ret
.stdout
.rstrip():
113 call('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
118 return unittest
.expectedFailure(func
)
122 def expectedFailureIfLinkFileFieldIsNotSet():
125 rc
= call('ip link add name dummy99 type dummy', stderr
=subprocess
.DEVNULL
)
127 ret
= run('udevadm info -w10s /sys/class/net/dummy99', stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
)
128 if ret
.returncode
== 0 and 'E: ID_NET_LINK_FILE=' in ret
.stdout
.rstrip():
130 call('ip link del dummy99')
135 return unittest
.expectedFailure(func
)
139 def expectedFailureIfNexthopIsNotAvailable():
141 rc
= call('ip nexthop list', stderr
=subprocess
.DEVNULL
)
145 return unittest
.expectedFailure(func
)
149 def expectedFailureIfAlternativeNameIsNotAvailable():
151 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
152 rc
= call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr
=subprocess
.DEVNULL
)
153 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
157 return unittest
.expectedFailure(func
)
161 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
163 call('rmmod netdevsim', stderr
=subprocess
.DEVNULL
)
164 rc
= call('modprobe netdevsim', stderr
=subprocess
.DEVNULL
)
166 return unittest
.expectedFailure(func
)
169 with
open('/sys/bus/netdevsim/new_device', mode
='w') as f
:
171 except Exception as error
:
172 return unittest
.expectedFailure(func
)
174 call('udevadm settle')
175 call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr
=subprocess
.DEVNULL
)
177 with
open('/sys/class/net/eni99np1/device/sriov_numvfs', mode
='w') as f
:
179 except Exception as error
:
180 call('rmmod netdevsim', stderr
=subprocess
.DEVNULL
)
181 return unittest
.expectedFailure(func
)
183 call('rmmod netdevsim', stderr
=subprocess
.DEVNULL
)
188 def expectedFailureIfCAKEIsNotAvailable():
190 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
191 rc
= call('tc qdisc add dev dummy98 parent root cake', stderr
=subprocess
.DEVNULL
)
192 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
196 return unittest
.expectedFailure(func
)
200 def expectedFailureIfPIEIsNotAvailable():
202 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
203 rc
= call('tc qdisc add dev dummy98 parent root pie', stderr
=subprocess
.DEVNULL
)
204 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
208 return unittest
.expectedFailure(func
)
212 def expectedFailureIfHHFIsNotAvailable():
214 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
215 rc
= call('tc qdisc add dev dummy98 parent root hhf', stderr
=subprocess
.DEVNULL
)
216 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
220 return unittest
.expectedFailure(func
)
224 def expectedFailureIfETSIsNotAvailable():
226 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
227 rc
= call('tc qdisc add dev dummy98 parent root ets bands 10', stderr
=subprocess
.DEVNULL
)
228 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
232 return unittest
.expectedFailure(func
)
236 def expectedFailureIfFQPIEIsNotAvailable():
238 call('ip link add dummy98 type dummy', stderr
=subprocess
.DEVNULL
)
239 rc
= call('tc qdisc add dev dummy98 parent root fq_pie', stderr
=subprocess
.DEVNULL
)
240 call('ip link del dummy98', stderr
=subprocess
.DEVNULL
)
244 return unittest
.expectedFailure(func
)
251 os
.makedirs(network_unit_file_path
, exist_ok
=True)
252 os
.makedirs(networkd_ci_path
, exist_ok
=True)
254 shutil
.rmtree(networkd_ci_path
)
255 copytree(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_path
)
257 for u
in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service',
258 'systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
259 'firewalld.service']:
260 if call(f
'systemctl is-active --quiet {u}') == 0:
261 check_output(f
'systemctl stop {u}')
262 running_units
.append(u
)
266 'StartLimitIntervalSec=0',
273 'ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + networkd_bin
,
277 drop_in
+= ['ExecStart=!!' + networkd_bin
]
279 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
281 drop_in
+= ['Environment=ASAN_OPTIONS="' + asan_options
+ '"']
283 drop_in
+= ['Environment=LSAN_OPTIONS="' + lsan_options
+ '"']
285 drop_in
+= ['Environment=UBSAN_OPTIONS="' + ubsan_options
+ '"']
286 if asan_options
or lsan_options
or ubsan_options
:
287 drop_in
+= ['SystemCallFilter=']
288 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
289 drop_in
+= ['MemoryDenyWriteExecute=no']
291 os
.makedirs('/run/systemd/system/systemd-networkd.service.d', exist_ok
=True)
292 with
open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode
='w') as f
:
293 f
.write('\n'.join(drop_in
))
301 drop_in
+= ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin
]
303 drop_in
+= ['ExecStart=!!' + resolved_bin
]
305 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
307 drop_in
+= ['Environment=ASAN_OPTIONS="' + asan_options
+ '"']
309 drop_in
+= ['Environment=LSAN_OPTIONS="' + lsan_options
+ '"']
311 drop_in
+= ['Environment=UBSAN_OPTIONS="' + ubsan_options
+ '"']
312 if asan_options
or lsan_options
or ubsan_options
:
313 drop_in
+= ['SystemCallFilter=']
314 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
315 drop_in
+= ['MemoryDenyWriteExecute=no']
317 os
.makedirs('/run/systemd/system/systemd-resolved.service.d', exist_ok
=True)
318 with
open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode
='w') as f
:
319 f
.write('\n'.join(drop_in
))
324 'ExecStart=!!' + udevd_bin
,
327 os
.makedirs('/run/systemd/system/systemd-udevd.service.d', exist_ok
=True)
328 with
open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode
='w') as f
:
329 f
.write('\n'.join(drop_in
))
331 check_output('systemctl daemon-reload')
332 print(check_output('systemctl cat systemd-networkd.service'))
333 print(check_output('systemctl cat systemd-resolved.service'))
334 print(check_output('systemctl cat systemd-udevd.service'))
335 check_output('systemctl restart systemd-resolved')
336 check_output('systemctl restart systemd-udevd')
338 def tearDownModule():
341 shutil
.rmtree(networkd_ci_path
)
343 for u
in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service']:
344 check_output(f
'systemctl stop {u}')
346 shutil
.rmtree('/run/systemd/system/systemd-networkd.service.d')
347 shutil
.rmtree('/run/systemd/system/systemd-resolved.service.d')
348 shutil
.rmtree('/run/systemd/system/systemd-udevd.service.d')
349 check_output('systemctl daemon-reload')
350 check_output('systemctl restart systemd-udevd.service')
352 for u
in running_units
:
353 check_output(f
'systemctl start {u}')
355 def read_link_attr(*args
):
356 with
open(os
.path
.join('/sys/class/net/', *args
)) as f
:
357 return f
.readline().strip()
359 def read_bridge_port_attr(bridge
, link
, attribute
):
360 path_bridge
= os
.path
.join('/sys/devices/virtual/net', bridge
)
361 path_port
= 'lower_' + link
+ '/brport'
362 path
= os
.path
.join(path_bridge
, path_port
)
364 with
open(os
.path
.join(path
, attribute
)) as f
:
365 return f
.readline().strip()
367 def link_exists(link
):
368 return os
.path
.exists(os
.path
.join('/sys/class/net', link
))
370 def remove_links(links
):
372 if link_exists(link
):
373 call('ip link del dev', link
)
376 def remove_fou_ports(ports
):
378 call('ip fou del port', port
, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
)
380 def remove_routing_policy_rule_tables(tables
):
384 rc
= call('ip rule del table', table
, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
)
386 def remove_routes(routes
):
387 for route_type
, addr
in routes
:
388 call('ip route del', route_type
, addr
, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
)
390 def remove_l2tp_tunnels(tunnel_ids
):
391 output
= check_output('ip l2tp show tunnel')
392 for tid
in tunnel_ids
:
393 words
='Tunnel ' + tid
+ ', encap'
395 call('ip l2tp del tunnel tid', tid
)
398 def read_ipv6_sysctl_attr(link
, attribute
):
399 with
open(os
.path
.join(os
.path
.join(network_sysctl_ipv6_path
, link
), attribute
)) as f
:
400 return f
.readline().strip()
402 def read_ipv4_sysctl_attr(link
, attribute
):
403 with
open(os
.path
.join(os
.path
.join(network_sysctl_ipv4_path
, link
), attribute
)) as f
:
404 return f
.readline().strip()
406 def copy_unit_to_networkd_unit_path(*units
, dropins
=True):
407 """Copy networkd unit files into the testbed.
409 Any networkd unit file type can be specified, as well as drop-in files.
411 By default, all drop-ins for a specified unit file are copied in;
412 to avoid that specify dropins=False.
414 When a drop-in file is specified, its unit file is also copied in automatically.
418 if dropins
and os
.path
.exists(os
.path
.join(networkd_ci_path
, unit
+ '.d')):
419 copytree(os
.path
.join(networkd_ci_path
, unit
+ '.d'), os
.path
.join(network_unit_file_path
, unit
+ '.d'))
420 if unit
.endswith('.conf'):
422 dropindir
= os
.path
.join(network_unit_file_path
, os
.path
.dirname(dropin
))
423 os
.makedirs(dropindir
, exist_ok
=True)
424 shutil
.copy(os
.path
.join(networkd_ci_path
, dropin
), dropindir
)
425 unit
= os
.path
.dirname(dropin
).rstrip('.d')
426 shutil
.copy(os
.path
.join(networkd_ci_path
, unit
), network_unit_file_path
)
428 def remove_unit_from_networkd_path(units
):
429 """Remove previously copied unit files from the testbed.
431 Drop-ins will be removed automatically.
434 if (os
.path
.exists(os
.path
.join(network_unit_file_path
, unit
))):
435 os
.remove(os
.path
.join(network_unit_file_path
, unit
))
436 if (os
.path
.exists(os
.path
.join(network_unit_file_path
, unit
+ '.d'))):
437 shutil
.rmtree(os
.path
.join(network_unit_file_path
, unit
+ '.d'))
439 def start_dnsmasq(additional_options
='', ipv4_range
='192.168.5.10,192.168.5.200', ipv6_range
='2600::10,2600::20', lease_time
='1h'):
440 dnsmasq_command
= f
'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options
441 check_output(dnsmasq_command
)
443 def stop_dnsmasq(pid_file
):
444 if os
.path
.exists(pid_file
):
445 with
open(pid_file
, 'r') as f
:
446 pid
= f
.read().rstrip(' \t\r\n\0')
447 os
.kill(int(pid
), signal
.SIGTERM
)
451 def search_words_in_dnsmasq_log(words
, show_all
=False):
452 if os
.path
.exists(dnsmasq_log_file
):
453 with
open (dnsmasq_log_file
) as in_file
:
454 contents
= in_file
.read()
457 for line
in contents
.splitlines():
460 print("%s, %s" % (words
, line
))
464 def remove_lease_file():
465 if os
.path
.exists(os
.path
.join(networkd_ci_path
, 'lease')):
466 os
.remove(os
.path
.join(networkd_ci_path
, 'lease'))
468 def remove_log_file():
469 if os
.path
.exists(dnsmasq_log_file
):
470 os
.remove(dnsmasq_log_file
)
472 def remove_networkd_state_files():
473 if os
.path
.exists(os
.path
.join(networkd_runtime_directory
, 'state')):
474 os
.remove(os
.path
.join(networkd_runtime_directory
, 'state'))
476 def stop_networkd(show_logs
=True, remove_state_files
=True):
478 invocation_id
= check_output('systemctl show systemd-networkd -p InvocationID --value')
479 check_output('systemctl stop systemd-networkd.socket')
480 check_output('systemctl stop systemd-networkd.service')
482 print(check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id
))
483 if remove_state_files
:
484 remove_networkd_state_files()
486 def start_networkd(sleep_sec
=0):
487 check_output('systemctl start systemd-networkd')
489 time
.sleep(sleep_sec
)
491 def restart_networkd(sleep_sec
=0, show_logs
=True, remove_state_files
=True):
492 stop_networkd(show_logs
, remove_state_files
)
493 start_networkd(sleep_sec
)
497 def check_link_exists(self
, link
):
498 self
.assertTrue(link_exists(link
))
500 def check_link_attr(self
, *args
):
501 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1]);
503 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
504 """Wait for the link to reach the specified operstate and/or setup state.
506 Specify None or '' for either operstate or setup_state to ignore that state.
507 This will recheck until the state conditions are met or the timeout expires.
509 If the link successfully matches the requested state, this returns True.
510 If this times out waiting for the link to match, the behavior depends on the
511 'fail_assert' parameter; if True, this causes a test assertion failure,
512 otherwise this returns False. The default is to cause assertion failure.
514 Note that this function matches on *exactly* the given operstate and setup_state.
515 To wait for a link to reach *or exceed* a given operstate, use wait_online().
522 for secs
in range(setup_timeout
+ 1):
523 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', link
, env
=env
)
525 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
527 # don't bother sleeping if time is up
528 if secs
< setup_timeout
:
531 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
534 def wait_online(self
, links_with_operstate
, timeout
='20s', bool_any
=False, setup_state
='configured', setup_timeout
=5):
535 """Wait for the link(s) to reach the specified operstate and/or setup state.
537 This is similar to wait_operstate() but can be used for multiple links,
538 and it also calls systemd-networkd-wait-online to wait for the given operstate.
539 The operstate should be specified in the link name, like 'eth0:degraded'.
540 If just a link name is provided, wait-online's default operstate to wait for is degraded.
542 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
543 'setup_timeout' controls the per-link timeout waiting for the setup_state.
545 Set 'bool_any' to True to wait for any (instead of all) of the given links.
546 If this is set, no setup_state checks are done.
548 Note that this function waits for the link(s) to reach *or exceed* the given operstate.
549 However, the setup_state, if specified, must be matched *exactly*.
551 This returns if the link(s) reached the requested operstate/setup_state; otherwise it
552 raises CalledProcessError or fails test assertion.
554 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
]
558 check_output(*args
, env
=env
)
559 except subprocess
.CalledProcessError
:
560 for link
in links_with_operstate
:
561 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', link
.split(':')[0], env
=env
)
564 if not bool_any
and setup_state
:
565 for link
in links_with_operstate
:
566 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
568 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
569 for i
in range(timeout_sec
):
572 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
573 if re
.search(address_regex
, output
) and 'tentative' not in output
:
576 self
.assertRegex(output
, address_regex
)
578 class NetworkctlTests(unittest
.TestCase
, Utilities
):
588 '11-dummy-mtu.netdev',
592 '25-address-static.network',
594 'netdev-link-local-addressing-yes.network',
598 remove_links(self
.links
)
599 stop_networkd(show_logs
=False)
602 remove_links(self
.links
)
603 remove_unit_from_networkd_path(self
.units
)
604 stop_networkd(show_logs
=True)
606 @expectedFailureIfAlternativeNameIsNotAvailable()
607 def test_altname(self
):
608 copy_unit_to_networkd_unit_path('netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
609 check_output('udevadm control --reload')
611 self
.wait_online(['dummy98:degraded'])
613 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
614 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
616 def test_reconfigure(self
):
617 copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
619 self
.wait_online(['dummy98:routable'])
621 output
= check_output('ip -4 address show dev dummy98')
623 self
.assertRegex(output
, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
624 self
.assertRegex(output
, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
625 self
.assertRegex(output
, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
627 check_output('ip address del 10.1.2.3/16 dev dummy98')
628 check_output('ip address del 10.1.2.4/16 dev dummy98')
629 check_output('ip address del 10.2.2.4/16 dev dummy98')
631 check_output(*networkctl_cmd
, 'reconfigure', 'dummy98', env
=env
)
632 self
.wait_online(['dummy98:routable'])
634 output
= check_output('ip -4 address show dev dummy98')
636 self
.assertRegex(output
, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
637 self
.assertRegex(output
, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
638 self
.assertRegex(output
, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
640 def test_reload(self
):
643 copy_unit_to_networkd_unit_path('11-dummy.netdev')
644 check_output(*networkctl_cmd
, 'reload', env
=env
)
645 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
647 copy_unit_to_networkd_unit_path('11-dummy.network')
648 check_output(*networkctl_cmd
, 'reload', env
=env
)
649 self
.wait_online(['test1:degraded'])
651 remove_unit_from_networkd_path(['11-dummy.network'])
652 check_output(*networkctl_cmd
, 'reload', env
=env
)
653 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
655 remove_unit_from_networkd_path(['11-dummy.netdev'])
656 check_output(*networkctl_cmd
, 'reload', env
=env
)
657 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
659 copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
660 check_output(*networkctl_cmd
, 'reload', env
=env
)
661 self
.wait_operstate('test1', 'degraded')
664 copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
667 self
.wait_online(['test1:degraded'])
669 output
= check_output(*networkctl_cmd
, 'list', env
=env
)
670 self
.assertRegex(output
, '1 lo ')
671 self
.assertRegex(output
, 'test1')
673 output
= check_output(*networkctl_cmd
, 'list', 'test1', env
=env
)
674 self
.assertNotRegex(output
, '1 lo ')
675 self
.assertRegex(output
, 'test1')
677 output
= check_output(*networkctl_cmd
, 'list', 'te*', env
=env
)
678 self
.assertNotRegex(output
, '1 lo ')
679 self
.assertRegex(output
, 'test1')
681 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'te*', env
=env
)
682 self
.assertNotRegex(output
, '1: lo ')
683 self
.assertRegex(output
, 'test1')
685 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'tes[a-z][0-9]', env
=env
)
686 self
.assertNotRegex(output
, '1: lo ')
687 self
.assertRegex(output
, 'test1')
690 copy_unit_to_networkd_unit_path('11-dummy-mtu.netdev', '11-dummy.network')
693 self
.wait_online(['test1:degraded'])
695 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
696 self
.assertRegex(output
, 'MTU: 1600')
699 copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
701 self
.wait_online(['test1:degraded'])
703 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
705 self
.assertRegex(output
, 'Type: ether')
707 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
709 self
.assertRegex(output
, 'Type: loopback')
711 @expectedFailureIfLinkFileFieldIsNotSet()
712 def test_udev_link_file(self
):
713 copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
715 self
.wait_online(['test1:degraded'])
717 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
719 self
.assertRegex(output
, r
'Link File: (/usr)?/lib/systemd/network/99-default.link')
720 self
.assertRegex(output
, r
'Network File: /run/systemd/network/11-dummy.network')
722 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'lo', env
=env
)
724 self
.assertRegex(output
, r
'Link File: (/usr)?/lib/systemd/network/99-default.link')
725 self
.assertRegex(output
, r
'Network File: n/a')
727 def test_delete_links(self
):
728 copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
729 '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
732 self
.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
734 check_output(*networkctl_cmd
, 'delete', 'test1', 'veth99', env
=env
)
735 self
.assertFalse(link_exists('test1'))
736 self
.assertFalse(link_exists('veth99'))
737 self
.assertFalse(link_exists('veth-peer'))
739 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
741 links_remove_earlier
= [
809 '10-dropin-test.netdev',
813 '13-not-match-udev-property.network',
814 '14-match-udev-property.network',
815 '15-name-conflict-test.netdev',
818 '21-vlan-test1.network',
821 '25-6rd-tunnel.netdev',
824 '25-bond-balanced-tlb.netdev',
826 '25-bridge-configure-without-carrier.network',
828 '25-erspan-tunnel-local-any.netdev',
829 '25-erspan-tunnel.netdev',
830 '25-fou-gretap.netdev',
832 '25-fou-ipip.netdev',
833 '25-fou-ipproto-gre.netdev',
834 '25-fou-ipproto-ipip.netdev',
837 '25-gretap-tunnel-local-any.netdev',
838 '25-gretap-tunnel.netdev',
839 '25-gre-tunnel-any-any.netdev',
840 '25-gre-tunnel-local-any.netdev',
841 '25-gre-tunnel-remote-any.netdev',
842 '25-gre-tunnel.netdev',
844 '25-ip6gretap-tunnel-local-any.netdev',
845 '25-ip6gretap-tunnel.netdev',
846 '25-ip6gre-tunnel-any-any.netdev',
847 '25-ip6gre-tunnel-local-any.netdev',
848 '25-ip6gre-tunnel-remote-any.netdev',
849 '25-ip6gre-tunnel.netdev',
850 '25-ip6tnl-tunnel-any-any.netdev',
851 '25-ip6tnl-tunnel-local-any.netdev',
852 '25-ip6tnl-tunnel-remote-any.netdev',
853 '25-ip6tnl-tunnel.netdev',
854 '25-ipip-tunnel-any-any.netdev',
855 '25-ipip-tunnel-independent.netdev',
856 '25-ipip-tunnel-independent-loopback.netdev',
857 '25-ipip-tunnel-local-any.netdev',
858 '25-ipip-tunnel-remote-any.netdev',
859 '25-ipip-tunnel.netdev',
862 '25-isatap-tunnel.netdev',
867 '25-sit-tunnel-any-any.netdev',
868 '25-sit-tunnel-local-any.netdev',
869 '25-sit-tunnel-remote-any.netdev',
870 '25-sit-tunnel.netdev',
873 '25-tunnel-local-any.network',
874 '25-tunnel-remote-any.network',
879 '25-vti6-tunnel-any-any.netdev',
880 '25-vti6-tunnel-local-any.netdev',
881 '25-vti6-tunnel-remote-any.netdev',
882 '25-vti6-tunnel.netdev',
883 '25-vti-tunnel-any-any.netdev',
884 '25-vti-tunnel-local-any.netdev',
885 '25-vti-tunnel-remote-any.netdev',
886 '25-vti-tunnel.netdev',
888 '25-vxlan-independent.netdev',
890 '25-wireguard-23-peers.netdev',
891 '25-wireguard-23-peers.network',
892 '25-wireguard-no-peer.netdev',
893 '25-wireguard-no-peer.network',
894 '25-wireguard-preshared-key.txt',
895 '25-wireguard-private-key.txt',
896 '25-wireguard.netdev',
897 '25-wireguard.network',
899 '25-xfrm-independent.netdev',
915 'netdev-link-local-addressing-yes.network',
919 'vxlan-test1.network',
929 remove_fou_ports(self
.fou_ports
)
930 remove_links(self
.links_remove_earlier
)
931 remove_links(self
.links
)
932 stop_networkd(show_logs
=False)
935 remove_fou_ports(self
.fou_ports
)
936 remove_links(self
.links_remove_earlier
)
937 remove_links(self
.links
)
938 remove_unit_from_networkd_path(self
.units
)
939 stop_networkd(show_logs
=True)
941 def test_dropin_and_name_conflict(self
):
942 copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
945 self
.wait_online(['dropin-test:off'], setup_state
='unmanaged')
947 output
= check_output('ip link show dropin-test')
949 self
.assertRegex(output
, '00:50:56:c0:00:28')
951 def test_match_udev_property(self
):
952 copy_unit_to_networkd_unit_path('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
954 self
.wait_online(['dummy98:routable'])
956 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
958 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
960 def test_wait_online_any(self
):
961 copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
964 self
.wait_online(['bridge99', 'test1:degraded'], bool_any
=True)
966 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
967 self
.wait_operstate('test1', 'degraded')
969 @expectedFailureIfModuleIsNotAvailable('bareudp')
970 def test_bareudp(self
):
971 copy_unit_to_networkd_unit_path('25-bareudp.netdev', 'netdev-link-local-addressing-yes.network')
974 self
.wait_online(['bareudp99:degraded'])
976 output
= check_output('ip -d link show bareudp99')
978 self
.assertRegex(output
, 'dstport 1000 ')
979 self
.assertRegex(output
, 'ethertype ip ')
981 def test_bridge(self
):
982 copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
985 self
.wait_online(['bridge99:no-carrier'])
987 tick
= os
.sysconf('SC_CLK_TCK')
988 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
989 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
990 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
991 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
992 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
993 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
994 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
995 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
996 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
998 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
1000 self
.assertRegex(output
, 'Priority: 9')
1001 self
.assertRegex(output
, 'STP: yes')
1002 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1004 def test_bond(self
):
1005 copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1008 self
.wait_online(['bond99:off', 'bond98:off'], setup_state
='unmanaged')
1010 self
.assertEqual('802.3ad 4', read_link_attr('bond99', 'bonding', 'mode'))
1011 self
.assertEqual('layer3+4 1', read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
1012 self
.assertEqual('1000', read_link_attr('bond99', 'bonding', 'miimon'))
1013 self
.assertEqual('fast 1', read_link_attr('bond99', 'bonding', 'lacp_rate'))
1014 self
.assertEqual('2000', read_link_attr('bond99', 'bonding', 'updelay'))
1015 self
.assertEqual('2000', read_link_attr('bond99', 'bonding', 'downdelay'))
1016 self
.assertEqual('4', read_link_attr('bond99', 'bonding', 'resend_igmp'))
1017 self
.assertEqual('1', read_link_attr('bond99', 'bonding', 'min_links'))
1018 self
.assertEqual('1218', read_link_attr('bond99', 'bonding', 'ad_actor_sys_prio'))
1019 self
.assertEqual('811', read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
1020 self
.assertEqual('00:11:22:33:44:55', read_link_attr('bond99', 'bonding', 'ad_actor_system'))
1022 self
.assertEqual('balance-tlb 5', read_link_attr('bond98', 'bonding', 'mode'))
1023 self
.assertEqual('1', read_link_attr('bond98', 'bonding', 'tlb_dynamic_lb'))
1025 def test_vlan(self
):
1026 copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev',
1027 '21-vlan.network', '21-vlan-test1.network')
1030 self
.wait_online(['test1:degraded', 'vlan99:routable'])
1032 output
= check_output('ip -d link show test1')
1034 self
.assertRegex(output
, ' mtu 2000 ')
1036 output
= check_output('ip -d link show vlan99')
1038 self
.assertRegex(output
, ' mtu 2000 ')
1039 self
.assertRegex(output
, 'REORDER_HDR')
1040 self
.assertRegex(output
, 'LOOSE_BINDING')
1041 self
.assertRegex(output
, 'GVRP')
1042 self
.assertRegex(output
, 'MVRP')
1043 self
.assertRegex(output
, ' id 99 ')
1045 output
= check_output('ip -4 address show dev test1')
1047 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1048 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1050 output
= check_output('ip -4 address show dev vlan99')
1052 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1054 def test_macvtap(self
):
1055 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1056 with self
.subTest(mode
=mode
):
1057 if mode
!= 'private':
1059 copy_unit_to_networkd_unit_path('21-macvtap.netdev', 'netdev-link-local-addressing-yes.network',
1060 '11-dummy.netdev', 'macvtap.network')
1061 with
open(os
.path
.join(network_unit_file_path
, '21-macvtap.netdev'), mode
='a') as f
:
1062 f
.write('[MACVTAP]\nMode=' + mode
)
1065 self
.wait_online(['macvtap99:degraded', 'test1:degraded'])
1067 output
= check_output('ip -d link show macvtap99')
1069 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1071 def test_macvlan(self
):
1072 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1073 with self
.subTest(mode
=mode
):
1074 if mode
!= 'private':
1076 copy_unit_to_networkd_unit_path('21-macvlan.netdev', 'netdev-link-local-addressing-yes.network',
1077 '11-dummy.netdev', 'macvlan.network')
1078 with
open(os
.path
.join(network_unit_file_path
, '21-macvlan.netdev'), mode
='a') as f
:
1079 f
.write('[MACVLAN]\nMode=' + mode
)
1082 self
.wait_online(['macvlan99:degraded', 'test1:degraded'])
1084 output
= check_output('ip -d link show test1')
1086 self
.assertRegex(output
, ' mtu 2000 ')
1088 output
= check_output('ip -d link show macvlan99')
1090 self
.assertRegex(output
, ' mtu 2000 ')
1091 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1093 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1094 def test_ipvlan(self
):
1095 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1096 with self
.subTest(mode
=mode
, flag
=flag
):
1099 copy_unit_to_networkd_unit_path('25-ipvlan.netdev', 'netdev-link-local-addressing-yes.network',
1100 '11-dummy.netdev', 'ipvlan.network')
1101 with
open(os
.path
.join(network_unit_file_path
, '25-ipvlan.netdev'), mode
='a') as f
:
1102 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1105 self
.wait_online(['ipvlan99:degraded', 'test1:degraded'])
1107 output
= check_output('ip -d link show ipvlan99')
1109 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1111 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1112 def test_ipvtap(self
):
1113 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1114 with self
.subTest(mode
=mode
, flag
=flag
):
1117 copy_unit_to_networkd_unit_path('25-ipvtap.netdev', 'netdev-link-local-addressing-yes.network',
1118 '11-dummy.netdev', 'ipvtap.network')
1119 with
open(os
.path
.join(network_unit_file_path
, '25-ipvtap.netdev'), mode
='a') as f
:
1120 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1123 self
.wait_online(['ipvtap99:degraded', 'test1:degraded'])
1125 output
= check_output('ip -d link show ipvtap99')
1127 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1129 def test_veth(self
):
1130 copy_unit_to_networkd_unit_path('25-veth.netdev', 'netdev-link-local-addressing-yes.network')
1133 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
1135 output
= check_output('ip -d link show veth99')
1137 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1138 output
= check_output('ip -d link show veth-peer')
1140 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1143 copy_unit_to_networkd_unit_path('25-tun.netdev')
1146 self
.wait_online(['tun99:off'], setup_state
='unmanaged')
1148 output
= check_output('ip -d link show tun99')
1150 # Old ip command does not support IFF_ flags
1151 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1154 copy_unit_to_networkd_unit_path('25-tap.netdev')
1157 self
.wait_online(['tap99:off'], setup_state
='unmanaged')
1159 output
= check_output('ip -d link show tap99')
1161 # Old ip command does not support IFF_ flags
1162 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1164 @expectedFailureIfModuleIsNotAvailable('vrf')
1166 copy_unit_to_networkd_unit_path('25-vrf.netdev', 'netdev-link-local-addressing-yes.network')
1169 self
.wait_online(['vrf99:carrier'])
1171 @expectedFailureIfModuleIsNotAvailable('vcan')
1172 def test_vcan(self
):
1173 copy_unit_to_networkd_unit_path('25-vcan.netdev', 'netdev-link-local-addressing-yes.network')
1176 self
.wait_online(['vcan99:carrier'])
1178 @expectedFailureIfModuleIsNotAvailable('vxcan')
1179 def test_vxcan(self
):
1180 copy_unit_to_networkd_unit_path('25-vxcan.netdev', 'netdev-link-local-addressing-yes.network')
1183 self
.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
1185 @expectedFailureIfModuleIsNotAvailable('wireguard')
1186 def test_wireguard(self
):
1187 copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
1188 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1189 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1190 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1192 self
.wait_online(['wg99:carrier', 'wg98:routable', 'wg97:carrier'])
1194 if shutil
.which('wg'):
1197 output
= check_output('wg show wg99 listen-port')
1198 self
.assertRegex(output
, '51820')
1199 output
= check_output('wg show wg99 fwmark')
1200 self
.assertRegex(output
, '0x4d2')
1201 output
= check_output('wg show wg99 allowed-ips')
1202 self
.assertRegex(output
, r
'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48')
1203 self
.assertRegex(output
, r
'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128')
1204 output
= check_output('wg show wg99 persistent-keepalive')
1205 self
.assertRegex(output
, r
'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20')
1206 output
= check_output('wg show wg99 endpoints')
1207 self
.assertRegex(output
, r
'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.27.3:51820')
1208 output
= check_output('wg show wg99 private-key')
1209 self
.assertRegex(output
, r
'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=')
1210 output
= check_output('wg show wg99 preshared-keys')
1211 self
.assertRegex(output
, r
'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=')
1212 self
.assertRegex(output
, r
'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=')
1214 output
= check_output('wg show wg98 private-key')
1215 self
.assertRegex(output
, r
'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=')
1217 output
= check_output('wg show wg97 listen-port')
1218 self
.assertRegex(output
, '51821')
1219 output
= check_output('wg show wg97 fwmark')
1220 self
.assertRegex(output
, '0x4d3')
1222 def test_geneve(self
):
1223 copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
1226 self
.wait_online(['geneve99:degraded'])
1228 output
= check_output('ip -d link show geneve99')
1230 self
.assertRegex(output
, '192.168.22.1')
1231 self
.assertRegex(output
, '6082')
1232 self
.assertRegex(output
, 'udpcsum')
1233 self
.assertRegex(output
, 'udp6zerocsumrx')
1235 def test_ipip_tunnel(self
):
1236 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ipip.network',
1237 '25-ipip-tunnel.netdev', '25-tunnel.network',
1238 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1239 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1240 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1242 self
.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
1244 output
= check_output('ip -d link show ipiptun99')
1246 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1247 output
= check_output('ip -d link show ipiptun98')
1249 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1250 output
= check_output('ip -d link show ipiptun97')
1252 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1253 output
= check_output('ip -d link show ipiptun96')
1255 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1257 def test_gre_tunnel(self
):
1258 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretun.network',
1259 '25-gre-tunnel.netdev', '25-tunnel.network',
1260 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1261 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1262 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1264 self
.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
1266 output
= check_output('ip -d link show gretun99')
1268 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1269 self
.assertRegex(output
, 'ikey 1.2.3.103')
1270 self
.assertRegex(output
, 'okey 1.2.4.103')
1271 self
.assertRegex(output
, 'iseq')
1272 self
.assertRegex(output
, 'oseq')
1273 output
= check_output('ip -d link show gretun98')
1275 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
1276 self
.assertRegex(output
, 'ikey 0.0.0.104')
1277 self
.assertRegex(output
, 'okey 0.0.0.104')
1278 self
.assertNotRegex(output
, 'iseq')
1279 self
.assertNotRegex(output
, 'oseq')
1280 output
= check_output('ip -d link show gretun97')
1282 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
1283 self
.assertRegex(output
, 'ikey 0.0.0.105')
1284 self
.assertRegex(output
, 'okey 0.0.0.105')
1285 self
.assertNotRegex(output
, 'iseq')
1286 self
.assertNotRegex(output
, 'oseq')
1287 output
= check_output('ip -d link show gretun96')
1289 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
1290 self
.assertRegex(output
, 'ikey 0.0.0.106')
1291 self
.assertRegex(output
, 'okey 0.0.0.106')
1292 self
.assertNotRegex(output
, 'iseq')
1293 self
.assertNotRegex(output
, 'oseq')
1295 def test_ip6gre_tunnel(self
):
1296 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretun.network',
1297 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
1298 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1299 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1300 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1303 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
1305 self
.check_link_exists('dummy98')
1306 self
.check_link_exists('ip6gretun99')
1307 self
.check_link_exists('ip6gretun98')
1308 self
.check_link_exists('ip6gretun97')
1309 self
.check_link_exists('ip6gretun96')
1311 output
= check_output('ip -d link show ip6gretun99')
1313 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1314 output
= check_output('ip -d link show ip6gretun98')
1316 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
1317 output
= check_output('ip -d link show ip6gretun97')
1319 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
1320 output
= check_output('ip -d link show ip6gretun96')
1322 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
1324 def test_gretap_tunnel(self
):
1325 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretap.network',
1326 '25-gretap-tunnel.netdev', '25-tunnel.network',
1327 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1329 self
.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
1331 output
= check_output('ip -d link show gretap99')
1333 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1334 self
.assertRegex(output
, 'ikey 0.0.0.106')
1335 self
.assertRegex(output
, 'okey 0.0.0.106')
1336 self
.assertRegex(output
, 'iseq')
1337 self
.assertRegex(output
, 'oseq')
1338 output
= check_output('ip -d link show gretap98')
1340 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
1341 self
.assertRegex(output
, 'ikey 0.0.0.107')
1342 self
.assertRegex(output
, 'okey 0.0.0.107')
1343 self
.assertRegex(output
, 'iseq')
1344 self
.assertRegex(output
, 'oseq')
1346 def test_ip6gretap_tunnel(self
):
1347 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretap.network',
1348 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
1349 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1351 self
.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
1353 output
= check_output('ip -d link show ip6gretap99')
1355 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1356 output
= check_output('ip -d link show ip6gretap98')
1358 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
1360 def test_vti_tunnel(self
):
1361 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti.network',
1362 '25-vti-tunnel.netdev', '25-tunnel.network',
1363 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1364 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1365 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1367 self
.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
1369 output
= check_output('ip -d link show vtitun99')
1371 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1372 output
= check_output('ip -d link show vtitun98')
1374 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
1375 output
= check_output('ip -d link show vtitun97')
1377 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
1378 output
= check_output('ip -d link show vtitun96')
1380 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
1382 def test_vti6_tunnel(self
):
1383 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti6.network',
1384 '25-vti6-tunnel.netdev', '25-tunnel.network',
1385 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1386 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
1388 self
.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
1390 output
= check_output('ip -d link show vti6tun99')
1392 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1393 output
= check_output('ip -d link show vti6tun98')
1395 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
1396 output
= check_output('ip -d link show vti6tun97')
1398 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1400 def test_ip6tnl_tunnel(self
):
1401 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6tnl.network',
1402 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
1403 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1404 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
1406 self
.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'dummy98:degraded'])
1408 output
= check_output('ip -d link show ip6tnl99')
1410 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
1411 output
= check_output('ip -d link show ip6tnl98')
1413 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
1414 output
= check_output('ip -d link show ip6tnl97')
1416 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
1418 def test_sit_tunnel(self
):
1419 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network',
1420 '25-sit-tunnel.netdev', '25-tunnel.network',
1421 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1422 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1423 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1425 self
.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
1427 output
= check_output('ip -d link show sittun99')
1429 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
1430 output
= check_output('ip -d link show sittun98')
1432 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
1433 output
= check_output('ip -d link show sittun97')
1435 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
1436 output
= check_output('ip -d link show sittun96')
1438 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
1440 def test_isatap_tunnel(self
):
1441 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'isatap.network',
1442 '25-isatap-tunnel.netdev', '25-tunnel.network')
1444 self
.wait_online(['isataptun99:routable', 'dummy98:degraded'])
1446 output
= check_output('ip -d link show isataptun99')
1448 self
.assertRegex(output
, "isatap ")
1450 def test_6rd_tunnel(self
):
1451 copy_unit_to_networkd_unit_path('12-dummy.netdev', '6rd.network',
1452 '25-6rd-tunnel.netdev', '25-tunnel.network')
1454 self
.wait_online(['sittun99:routable', 'dummy98:degraded'])
1456 output
= check_output('ip -d link show sittun99')
1458 self
.assertRegex(output
, '6rd-prefix 2602::/24')
1460 @expectedFailureIfERSPANModuleIsNotAvailable()
1461 def test_erspan_tunnel(self
):
1462 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'erspan.network',
1463 '25-erspan-tunnel.netdev', '25-tunnel.network',
1464 '25-erspan-tunnel-local-any.netdev', '25-tunnel-local-any.network')
1466 self
.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
1468 output
= check_output('ip -d link show erspan99')
1470 self
.assertRegex(output
, 'erspan remote 172.16.1.100 local 172.16.1.200')
1471 self
.assertRegex(output
, 'ikey 0.0.0.101')
1472 self
.assertRegex(output
, 'okey 0.0.0.101')
1473 self
.assertRegex(output
, 'iseq')
1474 self
.assertRegex(output
, 'oseq')
1475 output
= check_output('ip -d link show erspan98')
1477 self
.assertRegex(output
, 'erspan remote 172.16.1.100 local any')
1478 self
.assertRegex(output
, '102')
1479 self
.assertRegex(output
, 'ikey 0.0.0.102')
1480 self
.assertRegex(output
, 'okey 0.0.0.102')
1481 self
.assertRegex(output
, 'iseq')
1482 self
.assertRegex(output
, 'oseq')
1484 def test_tunnel_independent(self
):
1485 copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev', 'netdev-link-local-addressing-yes.network')
1488 self
.wait_online(['ipiptun99:carrier'])
1490 def test_tunnel_independent_loopback(self
):
1491 copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent-loopback.netdev', 'netdev-link-local-addressing-yes.network')
1494 self
.wait_online(['ipiptun99:carrier'])
1496 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
1497 def test_xfrm(self
):
1498 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'xfrm.network',
1499 '25-xfrm.netdev', 'netdev-link-local-addressing-yes.network')
1502 self
.wait_online(['xfrm99:degraded', 'dummy98:degraded'])
1504 output
= check_output('ip link show dev xfrm99')
1507 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
1508 def test_xfrm_independent(self
):
1509 copy_unit_to_networkd_unit_path('25-xfrm-independent.netdev', 'netdev-link-local-addressing-yes.network')
1512 self
.wait_online(['xfrm99:degraded'])
1514 @expectedFailureIfModuleIsNotAvailable('fou')
1516 # The following redundant check is necessary for CentOS CI.
1517 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
1518 self
.assertTrue(is_module_available('fou'))
1520 copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
1521 '25-fou-ipip.netdev', '25-fou-sit.netdev',
1522 '25-fou-gre.netdev', '25-fou-gretap.netdev')
1525 self
.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state
='unmanaged')
1527 output
= check_output('ip fou show')
1529 self
.assertRegex(output
, 'port 55555 ipproto 4')
1530 self
.assertRegex(output
, 'port 55556 ipproto 47')
1532 output
= check_output('ip -d link show ipiptun96')
1534 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
1535 output
= check_output('ip -d link show sittun96')
1537 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
1538 output
= check_output('ip -d link show gretun96')
1540 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
1541 output
= check_output('ip -d link show gretap96')
1543 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
1545 def test_vxlan(self
):
1546 copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
1547 '25-vxlan-independent.netdev', 'netdev-link-local-addressing-yes.network',
1548 '11-dummy.netdev', 'vxlan-test1.network')
1551 self
.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded'])
1553 output
= check_output('ip -d link show vxlan99')
1555 self
.assertRegex(output
, '999')
1556 self
.assertRegex(output
, '5555')
1557 self
.assertRegex(output
, 'l2miss')
1558 self
.assertRegex(output
, 'l3miss')
1559 self
.assertRegex(output
, 'udpcsum')
1560 self
.assertRegex(output
, 'udp6zerocsumtx')
1561 self
.assertRegex(output
, 'udp6zerocsumrx')
1562 self
.assertRegex(output
, 'remcsumtx')
1563 self
.assertRegex(output
, 'remcsumrx')
1564 self
.assertRegex(output
, 'gbp')
1566 output
= check_output('bridge fdb show dev vxlan99')
1568 self
.assertRegex(output
, '00:11:22:33:44:55 dst 10.0.0.5 self permanent')
1569 self
.assertRegex(output
, '00:11:22:33:44:66 dst 10.0.0.6 self permanent')
1570 self
.assertRegex(output
, '00:11:22:33:44:77 dst 10.0.0.7 self permanent')
1572 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'vxlan99', env
=env
)
1574 self
.assertRegex(output
, 'VNI: 999')
1575 self
.assertRegex(output
, 'Destination Port: 5555')
1576 self
.assertRegex(output
, 'Underlying Device: test1')
1578 output
= check_output('ip -d link show vxlan98')
1581 def test_macsec(self
):
1582 copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
1583 'macsec.network', '12-dummy.netdev')
1586 self
.wait_online(['dummy98:degraded', 'macsec99:routable'])
1588 output
= check_output('ip -d link show macsec99')
1590 self
.assertRegex(output
, 'macsec99@dummy98')
1591 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
1592 self
.assertRegex(output
, 'encrypt on')
1594 output
= check_output('ip macsec show macsec99')
1596 self
.assertRegex(output
, 'encrypt on')
1597 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
1598 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
1599 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
1600 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
1601 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
1602 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
1603 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
1604 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
1605 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
1606 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
1607 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
1609 def test_nlmon(self
):
1610 copy_unit_to_networkd_unit_path('25-nlmon.netdev', 'netdev-link-local-addressing-yes.network')
1613 self
.wait_online(['nlmon99:carrier'])
1615 @expectedFailureIfModuleIsNotAvailable('ifb')
1617 copy_unit_to_networkd_unit_path('25-ifb.netdev', 'netdev-link-local-addressing-yes.network')
1620 self
.wait_online(['ifb99:degraded'])
1622 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
1633 '25-l2tp-dummy.network',
1635 '25-l2tp-ip.netdev',
1636 '25-l2tp-udp.netdev']
1638 l2tp_tunnel_ids
= [ '10' ]
1641 remove_l2tp_tunnels(self
.l2tp_tunnel_ids
)
1642 remove_links(self
.links
)
1643 stop_networkd(show_logs
=False)
1646 remove_l2tp_tunnels(self
.l2tp_tunnel_ids
)
1647 remove_links(self
.links
)
1648 remove_unit_from_networkd_path(self
.units
)
1649 stop_networkd(show_logs
=True)
1651 @expectedFailureIfModuleIsNotAvailable('l2tp_eth')
1652 def test_l2tp_udp(self
):
1653 copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network',
1654 '25-l2tp-udp.netdev', '25-l2tp.network')
1657 self
.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
1659 output
= check_output('ip l2tp show tunnel tunnel_id 10')
1661 self
.assertRegex(output
, "Tunnel 10, encap UDP")
1662 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
1663 self
.assertRegex(output
, "Peer tunnel 11")
1664 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
1665 self
.assertRegex(output
, "UDP checksum: enabled")
1667 output
= check_output('ip l2tp show session tid 10 session_id 15')
1669 self
.assertRegex(output
, "Session 15 in tunnel 10")
1670 self
.assertRegex(output
, "Peer session 16, tunnel 11")
1671 self
.assertRegex(output
, "interface name: l2tp-ses1")
1673 output
= check_output('ip l2tp show session tid 10 session_id 17')
1675 self
.assertRegex(output
, "Session 17 in tunnel 10")
1676 self
.assertRegex(output
, "Peer session 18, tunnel 11")
1677 self
.assertRegex(output
, "interface name: l2tp-ses2")
1679 @expectedFailureIfModuleIsNotAvailable('l2tp_ip')
1680 def test_l2tp_ip(self
):
1681 copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network',
1682 '25-l2tp-ip.netdev', '25-l2tp.network')
1685 self
.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
1687 output
= check_output('ip l2tp show tunnel tunnel_id 10')
1689 self
.assertRegex(output
, "Tunnel 10, encap IP")
1690 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
1691 self
.assertRegex(output
, "Peer tunnel 12")
1693 output
= check_output('ip l2tp show session tid 10 session_id 25')
1695 self
.assertRegex(output
, "Session 25 in tunnel 10")
1696 self
.assertRegex(output
, "Peer session 26, tunnel 12")
1697 self
.assertRegex(output
, "interface name: l2tp-ses3")
1699 output
= check_output('ip l2tp show session tid 10 session_id 27')
1701 self
.assertRegex(output
, "Session 27 in tunnel 10")
1702 self
.assertRegex(output
, "Peer session 28, tunnel 12")
1703 self
.assertRegex(output
, "interface name: l2tp-ses4")
1705 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
1720 '23-active-slave.network',
1721 '24-keep-configuration-static.network',
1722 '24-search-domain.network',
1723 '25-address-dad-veth-peer.network',
1724 '25-address-dad-veth99.network',
1725 '25-address-link-section.network',
1726 '25-address-preferred-lifetime-zero.network',
1727 '25-address-static.network',
1728 '25-bind-carrier.network',
1729 '25-bond-active-backup-slave.netdev',
1730 '25-fibrule-invert.network',
1731 '25-fibrule-port-range.network',
1732 '25-fibrule-uidrange.network',
1733 '25-gre-tunnel-remote-any.netdev',
1734 '25-ip6gre-tunnel-remote-any.netdev',
1735 '25-ipv6-address-label-section.network',
1736 '25-link-local-addressing-no.network',
1737 '25-link-local-addressing-yes.network',
1738 '25-link-section-unmanaged.network',
1739 '25-neighbor-section.network',
1740 '25-neighbor-next.network',
1741 '25-neighbor-ipv6.network',
1742 '25-neighbor-ip-dummy.network',
1743 '25-neighbor-ip.network',
1744 '25-nexthop.network',
1745 '25-qdisc-cake.network',
1746 '25-qdisc-clsact-and-htb.network',
1747 '25-qdisc-drr.network',
1748 '25-qdisc-ets.network',
1749 '25-qdisc-fq_pie.network',
1750 '25-qdisc-hhf.network',
1751 '25-qdisc-ingress-netem-compat.network',
1752 '25-qdisc-pie.network',
1753 '25-qdisc-qfq.network',
1754 '25-prefix-route-with-vrf.network',
1755 '25-prefix-route-without-vrf.network',
1756 '25-route-ipv6-src.network',
1757 '25-route-static.network',
1758 '25-route-vrf.network',
1759 '25-gateway-static.network',
1760 '25-gateway-next-static.network',
1762 '25-sysctl-disable-ipv6.network',
1763 '25-sysctl.network',
1765 '25-veth-peer.network',
1769 '26-link-local-addressing-ipv6.network',
1770 'routing-policy-rule-dummy98.network',
1771 'routing-policy-rule-test1.network',
1772 'routing-policy-rule-reconfigure.network',
1775 routing_policy_rule_tables
= ['7', '8', '9', '1011']
1776 routes
= [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']]
1779 remove_routing_policy_rule_tables(self
.routing_policy_rule_tables
)
1780 remove_routes(self
.routes
)
1781 remove_links(self
.links
)
1782 stop_networkd(show_logs
=False)
1785 remove_routing_policy_rule_tables(self
.routing_policy_rule_tables
)
1786 remove_routes(self
.routes
)
1787 remove_links(self
.links
)
1788 remove_unit_from_networkd_path(self
.units
)
1789 stop_networkd(show_logs
=True)
1791 def test_address_static(self
):
1792 copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
1795 self
.wait_online(['dummy98:routable'])
1797 output
= check_output('ip -4 address show dev dummy98')
1799 self
.assertRegex(output
, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
1800 self
.assertRegex(output
, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
1801 self
.assertRegex(output
, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
1803 # test for ENOBUFS issue #17012
1804 for i
in range(1,254):
1805 self
.assertRegex(output
, f
'inet 10.3.3.{i}/16 brd 10.3.255.255')
1808 self
.assertNotRegex(output
, '10.10.0.1/16')
1809 self
.assertNotRegex(output
, '10.10.0.2/16')
1811 output
= check_output('ip -4 address show dev dummy98 label 32')
1812 self
.assertRegex(output
, 'inet 10.3.2.3/16 brd 10.3.255.255 scope global 32')
1814 output
= check_output('ip -4 address show dev dummy98 label 33')
1815 self
.assertRegex(output
, 'inet 10.4.2.3 peer 10.4.2.4/16 scope global 33')
1817 output
= check_output('ip -4 address show dev dummy98 label 34')
1818 self
.assertRegex(output
, 'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255 scope global 34')
1820 output
= check_output('ip -4 address show dev dummy98 label 35')
1821 self
.assertRegex(output
, 'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255 scope global 35')
1823 output
= check_output('ip -6 address show dev dummy98')
1825 self
.assertRegex(output
, 'inet6 2001:db8:0:f101::15/64 scope global')
1826 self
.assertRegex(output
, 'inet6 2001:db8:0:f101::16/64 scope global')
1827 self
.assertRegex(output
, 'inet6 2001:db8:0:f102::15/64 scope global')
1828 self
.assertRegex(output
, 'inet6 2001:db8:0:f102::16/64 scope global')
1829 self
.assertRegex(output
, 'inet6 2001:db8:0:f103::20 peer 2001:db8:0:f103::10/128 scope global')
1830 self
.assertRegex(output
, 'inet6 fd[0-9a-f:]*1/64 scope global')
1833 self
.wait_online(['dummy98:routable'])
1835 # test for ENOBUFS issue #17012
1836 output
= check_output('ip -4 address show dev dummy98')
1837 for i
in range(1,254):
1838 self
.assertRegex(output
, f
'inet 10.3.3.{i}/16 brd 10.3.255.255')
1840 def test_address_preferred_lifetime_zero_ipv6(self
):
1841 copy_unit_to_networkd_unit_path('25-address-preferred-lifetime-zero.network', '12-dummy.netdev')
1844 self
.wait_online(['dummy98:routable'])
1846 output
= check_output('ip address show dummy98')
1848 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope link deprecated dummy98')
1849 self
.assertRegex(output
, 'inet6 2001:db8:0:f101::1/64 scope global')
1851 output
= check_output('ip route show dev dummy98')
1853 self
.assertRegex(output
, 'default via 20.20.20.1 proto static')
1855 def test_address_dad(self
):
1856 copy_unit_to_networkd_unit_path('25-address-dad-veth99.network', '25-address-dad-veth-peer.network',
1859 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
1861 output
= check_output('ip -4 address show dev veth99')
1863 self
.assertRegex(output
, '192.168.100.10/24')
1865 output
= check_output('ip -4 address show dev veth-peer')
1867 self
.assertNotRegex(output
, '192.168.100.10/24')
1869 @expectedFailureIfModuleIsNotAvailable('vrf')
1870 def test_prefix_route(self
):
1871 copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev',
1872 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
1873 '25-vrf.netdev', '25-vrf.network')
1874 for trial
in range(2):
1880 self
.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
1882 output
= check_output('ip route show table 42 dev dummy98')
1883 print('### ip route show table 42 dev dummy98')
1885 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
1886 self
.assertRegex(output
, 'broadcast 10.20.33.0 proto kernel scope link src 10.20.33.1')
1887 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
1888 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
1889 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
1890 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
1891 self
.assertRegex(output
, 'broadcast 10.20.55.0 proto kernel scope link src 10.20.55.1')
1892 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
1893 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
1894 output
= check_output('ip -6 route show table 42 dev dummy98')
1895 print('### ip -6 route show table 42 dev dummy98')
1899 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
1900 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
1901 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
1902 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
1903 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
1904 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
1905 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
1906 self
.assertRegex(output
, 'ff00::/8 metric 256 pref medium')
1910 output
= check_output('ip route show dev test1')
1911 print('### ip route show dev test1')
1913 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
1914 output
= check_output('ip route show table local dev test1')
1915 print('### ip route show table local dev test1')
1917 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
1918 self
.assertRegex(output
, 'broadcast 10.21.33.0 proto kernel scope link src 10.21.33.1')
1919 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
1920 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
1921 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
1922 self
.assertRegex(output
, 'broadcast 10.21.55.0 proto kernel scope link src 10.21.55.1')
1923 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
1924 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
1925 output
= check_output('ip -6 route show dev test1')
1926 print('### ip -6 route show dev test1')
1928 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
1929 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
1930 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
1931 output
= check_output('ip -6 route show table local dev test1')
1932 print('### ip -6 route show table local dev test1')
1934 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
1935 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
1936 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
1937 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
1938 self
.assertRegex(output
, 'ff00::/8 metric 256 pref medium')
1940 def test_configure_without_carrier(self
):
1941 copy_unit_to_networkd_unit_path('11-dummy.netdev')
1943 self
.wait_operstate('test1', 'off', '')
1944 check_output('ip link set dev test1 up carrier off')
1946 copy_unit_to_networkd_unit_path('25-test1.network.d/configure-without-carrier.conf', dropins
=False)
1948 self
.wait_online(['test1:no-carrier'])
1950 carrier_map
= {'on': '1', 'off': '0'}
1951 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
1952 for carrier
in ['off', 'on', 'off']:
1953 with self
.subTest(carrier
=carrier
):
1954 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
1955 check_output(f
'ip link set dev test1 carrier {carrier}')
1956 self
.wait_online([f
'test1:{routable_map[carrier]}'])
1958 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1960 self
.assertRegex(output
, '192.168.0.15')
1961 self
.assertRegex(output
, '192.168.0.1')
1962 self
.assertRegex(output
, routable_map
[carrier
])
1964 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
1965 copy_unit_to_networkd_unit_path('11-dummy.netdev')
1967 self
.wait_operstate('test1', 'off', '')
1968 check_output('ip link set dev test1 up carrier off')
1970 copy_unit_to_networkd_unit_path('25-test1.network')
1972 self
.wait_online(['test1:no-carrier'])
1974 carrier_map
= {'on': '1', 'off': '0'}
1975 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
1976 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
1977 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
1978 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
1979 check_output(f
'ip link set dev test1 carrier {carrier}')
1980 self
.wait_online([f
'test1:{routable_map[carrier]}'])
1982 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'test1', env
=env
)
1985 self
.assertRegex(output
, '192.168.0.15')
1986 self
.assertRegex(output
, '192.168.0.1')
1988 self
.assertNotRegex(output
, '192.168.0.15')
1989 self
.assertNotRegex(output
, '192.168.0.1')
1990 self
.assertRegex(output
, routable_map
[carrier
])
1992 def test_routing_policy_rule(self
):
1993 copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev')
1995 self
.wait_online(['test1:degraded'])
1997 output
= check_output('ip rule list iif test1 priority 111')
1999 self
.assertRegex(output
, '111:')
2000 self
.assertRegex(output
, 'from 192.168.100.18')
2001 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
2002 self
.assertRegex(output
, 'iif test1')
2003 self
.assertRegex(output
, 'oif test1')
2004 self
.assertRegex(output
, 'lookup 7')
2006 output
= check_output('ip rule list iif test1 priority 101')
2008 self
.assertRegex(output
, '101:')
2009 self
.assertRegex(output
, 'from all')
2010 self
.assertRegex(output
, 'iif test1')
2011 self
.assertRegex(output
, 'lookup 9')
2013 output
= check_output('ip -6 rule list iif test1 priority 100')
2015 self
.assertRegex(output
, '100:')
2016 self
.assertRegex(output
, 'from all')
2017 self
.assertRegex(output
, 'iif test1')
2018 self
.assertRegex(output
, 'lookup 8')
2020 def test_routing_policy_rule_issue_11280(self
):
2021 copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev',
2022 'routing-policy-rule-dummy98.network', '12-dummy.netdev')
2024 for trial
in range(3):
2025 # Remove state files only first time
2027 self
.wait_online(['test1:degraded', 'dummy98:degraded'])
2030 output
= check_output('ip rule list table 7')
2032 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
2034 output
= check_output('ip rule list table 8')
2036 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
2038 stop_networkd(remove_state_files
=False)
2040 def test_routing_policy_rule_reconfigure(self
):
2041 copy_unit_to_networkd_unit_path('routing-policy-rule-reconfigure.network', '11-dummy.netdev')
2043 self
.wait_online(['test1:degraded'])
2045 output
= check_output('ip rule list table 1011')
2047 self
.assertRegex(output
, '10111: from all fwmark 0x3f3 lookup 1011')
2048 self
.assertRegex(output
, '10112: from all oif test1 lookup 1011')
2049 self
.assertRegex(output
, '10113: from all iif test1 lookup 1011')
2050 self
.assertRegex(output
, '10114: from 192.168.8.254 lookup 1011')
2052 run('ip rule delete priority 10111')
2053 run('ip rule delete priority 10112')
2054 run('ip rule delete priority 10113')
2055 run('ip rule delete priority 10114')
2056 run('ip rule delete priority 10115')
2058 output
= check_output('ip rule list table 1011')
2060 self
.assertEqual(output
, '')
2062 run(*networkctl_cmd
, 'reconfigure', 'test1', env
=env
)
2064 self
.wait_online(['test1:degraded'])
2066 output
= check_output('ip rule list table 1011')
2068 self
.assertRegex(output
, '10111: from all fwmark 0x3f3 lookup 1011')
2069 self
.assertRegex(output
, '10112: from all oif test1 lookup 1011')
2070 self
.assertRegex(output
, '10113: from all iif test1 lookup 1011')
2071 self
.assertRegex(output
, '10114: from 192.168.8.254 lookup 1011')
2073 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
2074 def test_routing_policy_rule_port_range(self
):
2075 copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
2077 self
.wait_online(['test1:degraded'])
2079 output
= check_output('ip rule')
2081 self
.assertRegex(output
, '111')
2082 self
.assertRegex(output
, 'from 192.168.100.18')
2083 self
.assertRegex(output
, '1123-1150')
2084 self
.assertRegex(output
, '3224-3290')
2085 self
.assertRegex(output
, 'tcp')
2086 self
.assertRegex(output
, 'lookup 7')
2088 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
2089 def test_routing_policy_rule_invert(self
):
2090 copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev')
2092 self
.wait_online(['test1:degraded'])
2094 output
= check_output('ip rule')
2096 self
.assertRegex(output
, '111')
2097 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
2098 self
.assertRegex(output
, 'tcp')
2099 self
.assertRegex(output
, 'lookup 7')
2101 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
2102 def test_routing_policy_rule_uidrange(self
):
2103 copy_unit_to_networkd_unit_path('25-fibrule-uidrange.network', '11-dummy.netdev')
2105 self
.wait_online(['test1:degraded'])
2107 output
= check_output('ip rule')
2109 self
.assertRegex(output
, '111')
2110 self
.assertRegex(output
, 'from 192.168.100.18')
2111 self
.assertRegex(output
, 'lookup 7')
2112 self
.assertRegex(output
, 'uidrange 100-200')
2114 def test_route_static(self
):
2115 copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
2117 self
.wait_online(['dummy98:routable'])
2119 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
2122 print('### ip -6 route show dev dummy98')
2123 output
= check_output('ip -6 route show dev dummy98')
2125 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
2126 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
2128 print('### ip -6 route show dev dummy98 default')
2129 output
= check_output('ip -6 route show dev dummy98 default')
2131 self
.assertRegex(output
, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium')
2133 print('### ip -4 route show dev dummy98')
2134 output
= check_output('ip -4 route show dev dummy98')
2136 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
2137 self
.assertRegex(output
, '149.10.124.64 proto static scope link')
2138 self
.assertRegex(output
, '169.254.0.0/16 proto static scope link metric 2048')
2139 self
.assertRegex(output
, '192.168.1.1 proto static initcwnd 20')
2140 self
.assertRegex(output
, '192.168.1.2 proto static initrwnd 30')
2141 self
.assertRegex(output
, 'multicast 149.10.123.4 proto static')
2143 print('### ip -4 route show dev dummy98 default')
2144 output
= check_output('ip -4 route show dev dummy98 default')
2146 self
.assertRegex(output
, 'default via 149.10.125.65 proto static onlink')
2147 self
.assertRegex(output
, 'default via 149.10.124.64 proto static')
2148 self
.assertRegex(output
, 'default proto static')
2150 print('### ip -4 route show table local dev dummy98')
2151 output
= check_output('ip -4 route show table local dev dummy98')
2153 self
.assertRegex(output
, 'local 149.10.123.1 proto static scope host')
2154 self
.assertRegex(output
, 'anycast 149.10.123.2 proto static scope link')
2155 self
.assertRegex(output
, 'broadcast 149.10.123.3 proto static scope link')
2157 print('### ip route show type blackhole')
2158 output
= check_output('ip route show type blackhole')
2160 self
.assertRegex(output
, 'blackhole 202.54.1.2 proto static')
2162 print('### ip route show type unreachable')
2163 output
= check_output('ip route show type unreachable')
2165 self
.assertRegex(output
, 'unreachable 202.54.1.3 proto static')
2167 print('### ip route show type prohibit')
2168 output
= check_output('ip route show type prohibit')
2170 self
.assertRegex(output
, 'prohibit 202.54.1.4 proto static')
2172 print('### ip route show 192.168.10.1')
2173 output
= check_output('ip route show 192.168.10.1')
2175 self
.assertRegex(output
, '192.168.10.1 proto static')
2176 self
.assertRegex(output
, 'nexthop via 149.10.124.59 dev dummy98 weight 10')
2177 self
.assertRegex(output
, 'nexthop via 149.10.124.60 dev dummy98 weight 5')
2179 print('### ip route show 192.168.10.2')
2180 output
= check_output('ip route show 192.168.10.2')
2182 # old ip command does not show IPv6 gateways...
2183 self
.assertRegex(output
, '192.168.10.2 proto static')
2184 self
.assertRegex(output
, 'nexthop')
2185 self
.assertRegex(output
, 'dev dummy98 weight 10')
2186 self
.assertRegex(output
, 'dev dummy98 weight 5')
2188 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
2189 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
2191 # old ip command does not show 'nexthop' keyword and weight...
2192 self
.assertRegex(output
, '2001:1234:5:7fff:ff:ff:ff:ff')
2193 self
.assertRegex(output
, 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98')
2194 self
.assertRegex(output
, 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98')
2196 @expectedFailureIfModuleIsNotAvailable('vrf')
2197 def test_route_vrf(self
):
2198 copy_unit_to_networkd_unit_path('25-route-vrf.network', '12-dummy.netdev',
2199 '25-vrf.netdev', '25-vrf.network')
2201 self
.wait_online(['dummy98:routable', 'vrf99:carrier'])
2203 output
= check_output('ip route show vrf vrf99')
2205 self
.assertRegex(output
, 'default via 192.168.100.1')
2207 output
= check_output('ip route show')
2209 self
.assertNotRegex(output
, 'default via 192.168.100.1')
2211 def test_gateway_reconfigure(self
):
2212 copy_unit_to_networkd_unit_path('25-gateway-static.network', '12-dummy.netdev')
2214 self
.wait_online(['dummy98:routable'])
2215 print('### ip -4 route show dev dummy98 default')
2216 output
= check_output('ip -4 route show dev dummy98 default')
2218 self
.assertRegex(output
, 'default via 149.10.124.59 proto static')
2219 self
.assertNotRegex(output
, '149.10.124.60')
2221 remove_unit_from_networkd_path(['25-gateway-static.network'])
2222 copy_unit_to_networkd_unit_path('25-gateway-next-static.network')
2224 self
.wait_online(['dummy98:routable'])
2225 print('### ip -4 route show dev dummy98 default')
2226 output
= check_output('ip -4 route show dev dummy98 default')
2228 self
.assertNotRegex(output
, '149.10.124.59')
2229 self
.assertRegex(output
, 'default via 149.10.124.60 proto static')
2231 def test_ip_route_ipv6_src_route(self
):
2232 # a dummy device does not make the addresses go through tentative state, so we
2233 # reuse a bond from an earlier test, which does make the addresses go through
2234 # tentative state, and do our test on that
2235 copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2237 self
.wait_online(['dummy98:enslaved', 'bond199:routable'])
2239 output
= check_output('ip -6 route list dev bond199')
2241 self
.assertRegex(output
, 'abcd::/16')
2242 self
.assertRegex(output
, 'src')
2243 self
.assertRegex(output
, '2001:1234:56:8f63::2')
2245 def test_ip_link_mac_address(self
):
2246 copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
2248 self
.wait_online(['dummy98:degraded'])
2250 output
= check_output('ip link show dummy98')
2252 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
2254 def test_ip_link_unmanaged(self
):
2255 copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
2258 self
.check_link_exists('dummy98')
2260 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
2262 def test_ipv6_address_label(self
):
2263 copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
2265 self
.wait_online(['dummy98:degraded'])
2267 output
= check_output('ip addrlabel list')
2269 self
.assertRegex(output
, '2004:da8:1::/64')
2271 def test_neighbor_section(self
):
2272 copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
2274 self
.wait_online(['dummy98:degraded'], timeout
='40s')
2276 print('### ip neigh list dev dummy98')
2277 output
= check_output('ip neigh list dev dummy98')
2279 self
.assertRegex(output
, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2280 self
.assertRegex(output
, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
2282 def test_neighbor_reconfigure(self
):
2283 copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
2285 self
.wait_online(['dummy98:degraded'], timeout
='40s')
2287 print('### ip neigh list dev dummy98')
2288 output
= check_output('ip neigh list dev dummy98')
2290 self
.assertRegex(output
, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2291 self
.assertRegex(output
, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
2293 remove_unit_from_networkd_path(['25-neighbor-section.network'])
2294 copy_unit_to_networkd_unit_path('25-neighbor-next.network')
2296 self
.wait_online(['dummy98:degraded'], timeout
='40s')
2297 print('### ip neigh list dev dummy98')
2298 output
= check_output('ip neigh list dev dummy98')
2300 self
.assertNotRegex(output
, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
2301 self
.assertRegex(output
, '192.168.10.1.*00:00:5e:00:02:66.*PERMANENT')
2302 self
.assertNotRegex(output
, '2004:da8:1::1.*PERMANENT')
2304 def test_neighbor_gre(self
):
2305 copy_unit_to_networkd_unit_path('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
2306 '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
2308 self
.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout
='40s')
2310 output
= check_output('ip neigh list dev gretun97')
2312 self
.assertRegex(output
, '10.0.0.22 lladdr 10.65.223.239 PERMANENT')
2314 output
= check_output('ip neigh list dev ip6gretun97')
2316 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
2318 def test_link_local_addressing(self
):
2319 copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev',
2320 '25-link-local-addressing-no.network', '12-dummy.netdev')
2322 self
.wait_online(['test1:degraded', 'dummy98:carrier'])
2324 output
= check_output('ip address show dev test1')
2326 self
.assertRegex(output
, 'inet .* scope link')
2327 self
.assertRegex(output
, 'inet6 .* scope link')
2329 output
= check_output('ip address show dev dummy98')
2331 self
.assertNotRegex(output
, 'inet6* .* scope link')
2334 Documentation/networking/ip-sysctl.txt
2336 addr_gen_mode - INTEGER
2337 Defines how link-local and autoconf addresses are generated.
2339 0: generate address based on EUI64 (default)
2340 1: do no generate a link-local address, use EUI64 for addresses generated
2342 2: generate stable privacy addresses, using the secret from
2343 stable_secret (RFC7217)
2344 3: generate stable privacy addresses, using a random secret if unset
2347 test1_addr_gen_mode
= ''
2348 if os
.path
.exists(os
.path
.join(os
.path
.join(network_sysctl_ipv6_path
, 'test1'), 'stable_secret')):
2349 with
open(os
.path
.join(os
.path
.join(network_sysctl_ipv6_path
, 'test1'), 'stable_secret')) as f
:
2353 # if stable_secret is unset, then EIO is returned
2354 test1_addr_gen_mode
= '0'
2356 test1_addr_gen_mode
= '2'
2358 test1_addr_gen_mode
= '0'
2360 if os
.path
.exists(os
.path
.join(os
.path
.join(network_sysctl_ipv6_path
, 'test1'), 'addr_gen_mode')):
2361 self
.assertEqual(read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), test1_addr_gen_mode
)
2363 if os
.path
.exists(os
.path
.join(os
.path
.join(network_sysctl_ipv6_path
, 'dummy98'), 'addr_gen_mode')):
2364 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1')
2366 def test_link_local_addressing_remove_ipv6ll(self
):
2367 copy_unit_to_networkd_unit_path('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
2369 self
.wait_online(['dummy98:degraded'])
2371 output
= check_output('ip address show dev dummy98')
2373 self
.assertRegex(output
, 'inet6 .* scope link')
2375 copy_unit_to_networkd_unit_path('25-link-local-addressing-no.network')
2377 self
.wait_online(['dummy98:carrier'])
2379 output
= check_output('ip address show dev dummy98')
2381 self
.assertNotRegex(output
, 'inet6* .* scope link')
2383 def test_sysctl(self
):
2384 copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')
2386 self
.wait_online(['dummy98:degraded'])
2388 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
2389 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
2390 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
2391 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'hop_limit'), '5')
2392 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
2393 self
.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
2394 self
.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
2395 self
.assertEqual(read_ipv4_sysctl_attr('dummy98', 'accept_local'), '1')
2397 def test_sysctl_disable_ipv6(self
):
2398 copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
2400 print('## Disable ipv6')
2401 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
2402 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
2405 self
.wait_online(['dummy98:routable'])
2407 output
= check_output('ip -4 address show dummy98')
2409 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
2410 output
= check_output('ip -6 address show dummy98')
2412 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
2413 self
.assertRegex(output
, 'inet6 .* scope link')
2414 output
= check_output('ip -4 route show dev dummy98')
2416 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
2417 output
= check_output('ip -6 route show dev dummy98')
2419 self
.assertRegex(output
, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
2421 check_output('ip link del dummy98')
2423 print('## Enable ipv6')
2424 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
2425 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
2428 self
.wait_online(['dummy98:routable'])
2430 output
= check_output('ip -4 address show dummy98')
2432 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
2433 output
= check_output('ip -6 address show dummy98')
2435 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
2436 self
.assertRegex(output
, 'inet6 .* scope link')
2437 output
= check_output('ip -4 route show dev dummy98')
2439 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
2440 output
= check_output('ip -6 route show dev dummy98')
2442 self
.assertRegex(output
, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
2444 def test_bind_carrier(self
):
2445 check_output('ip link add dummy98 type dummy')
2446 check_output('ip link set dummy98 up')
2449 copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
2451 self
.wait_online(['test1:routable'])
2453 output
= check_output('ip address show test1')
2455 self
.assertRegex(output
, 'UP,LOWER_UP')
2456 self
.assertRegex(output
, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
2457 self
.wait_operstate('test1', 'routable')
2459 check_output('ip link add dummy99 type dummy')
2460 check_output('ip link set dummy99 up')
2462 output
= check_output('ip address show test1')
2464 self
.assertRegex(output
, 'UP,LOWER_UP')
2465 self
.assertRegex(output
, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
2466 self
.wait_operstate('test1', 'routable')
2468 check_output('ip link del dummy98')
2470 output
= check_output('ip address show test1')
2472 self
.assertRegex(output
, 'UP,LOWER_UP')
2473 self
.assertRegex(output
, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
2474 self
.wait_operstate('test1', 'routable')
2476 check_output('ip link set dummy99 down')
2478 output
= check_output('ip address show test1')
2480 self
.assertNotRegex(output
, 'UP,LOWER_UP')
2481 self
.assertRegex(output
, 'DOWN')
2482 self
.assertNotRegex(output
, '192.168.10')
2483 self
.wait_operstate('test1', 'off')
2485 check_output('ip link set dummy99 up')
2487 output
= check_output('ip address show test1')
2489 self
.assertRegex(output
, 'UP,LOWER_UP')
2490 self
.assertRegex(output
, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
2491 self
.wait_operstate('test1', 'routable')
2493 def test_domain(self
):
2494 copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
2496 self
.wait_online(['dummy98:routable'])
2498 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'dummy98', env
=env
)
2500 self
.assertRegex(output
, 'Address: 192.168.42.100')
2501 self
.assertRegex(output
, 'DNS: 192.168.42.1')
2502 self
.assertRegex(output
, 'Search Domains: one')
2504 def test_keep_configuration_static(self
):
2505 check_output('systemctl stop systemd-networkd.socket')
2506 check_output('systemctl stop systemd-networkd.service')
2508 check_output('ip link add name dummy98 type dummy')
2509 check_output('ip address add 10.1.2.3/16 dev dummy98')
2510 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
2511 output
= check_output('ip address show dummy98')
2513 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
2514 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
2515 output
= check_output('ip route show dev dummy98')
2518 copy_unit_to_networkd_unit_path('24-keep-configuration-static.network')
2520 self
.wait_online(['dummy98:routable'])
2522 output
= check_output('ip address show dummy98')
2524 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
2525 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
2527 @expectedFailureIfNexthopIsNotAvailable()
2528 def test_nexthop(self
):
2529 copy_unit_to_networkd_unit_path('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network')
2531 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
2533 output
= check_output('ip nexthop list dev veth99')
2535 self
.assertRegex(output
, '192.168.5.1')
2537 def test_qdisc(self
):
2538 copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
2539 '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
2540 check_output('modprobe sch_teql max_equalizers=2')
2543 self
.wait_online(['dummy98:routable', 'test1:routable'])
2545 output
= check_output('tc qdisc show dev test1')
2547 self
.assertRegex(output
, 'qdisc netem')
2548 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
2549 self
.assertRegex(output
, 'qdisc ingress')
2551 output
= check_output('tc qdisc show dev dummy98')
2553 self
.assertRegex(output
, 'qdisc clsact')
2555 self
.assertRegex(output
, 'qdisc htb 2: root')
2556 self
.assertRegex(output
, r
'default (0x30|30)')
2558 self
.assertRegex(output
, 'qdisc netem 30: parent 2:30')
2559 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
2560 self
.assertRegex(output
, 'qdisc fq_codel')
2561 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')
2563 self
.assertRegex(output
, 'qdisc teql1 31: parent 2:31')
2565 self
.assertRegex(output
, 'qdisc fq 32: parent 2:32')
2566 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
2567 self
.assertRegex(output
, 'quantum 1500')
2568 self
.assertRegex(output
, 'initial_quantum 13000')
2569 self
.assertRegex(output
, 'maxrate 1Mbit')
2571 self
.assertRegex(output
, 'qdisc codel 33: parent 2:33')
2572 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
2574 self
.assertRegex(output
, 'qdisc fq_codel 34: parent 2:34')
2575 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')
2577 self
.assertRegex(output
, 'qdisc tbf 35: parent 2:35')
2578 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
2580 self
.assertRegex(output
, 'qdisc sfq 36: parent 2:36')
2581 self
.assertRegex(output
, 'perturb 5sec')
2583 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
2584 self
.assertRegex(output
, 'limit 100000p')
2586 self
.assertRegex(output
, 'qdisc gred 38: parent 2:38')
2587 self
.assertRegex(output
, 'vqs 12 default 10 grio')
2589 self
.assertRegex(output
, 'qdisc sfb 39: parent 2:39')
2590 self
.assertRegex(output
, 'limit 200000')
2592 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
2593 self
.assertRegex(output
, 'limit 1000000')
2595 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
2596 self
.assertRegex(output
, 'limit 1023p')
2598 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
2600 output
= check_output('tc -d class show dev dummy98')
2602 self
.assertRegex(output
, 'class htb 2:30 root leaf 30:')
2603 self
.assertRegex(output
, 'class htb 2:31 root leaf 31:')
2604 self
.assertRegex(output
, 'class htb 2:32 root leaf 32:')
2605 self
.assertRegex(output
, 'class htb 2:33 root leaf 33:')
2606 self
.assertRegex(output
, 'class htb 2:34 root leaf 34:')
2607 self
.assertRegex(output
, 'class htb 2:35 root leaf 35:')
2608 self
.assertRegex(output
, 'class htb 2:36 root leaf 36:')
2609 self
.assertRegex(output
, 'class htb 2:37 root leaf 37:')
2610 self
.assertRegex(output
, 'class htb 2:38 root leaf 38:')
2611 self
.assertRegex(output
, 'class htb 2:39 root leaf 39:')
2612 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a:')
2613 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b:')
2614 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c:')
2615 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
2616 self
.assertRegex(output
, 'burst 123456')
2617 self
.assertRegex(output
, 'cburst 123457')
2619 def test_qdisc2(self
):
2620 copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev',
2621 '25-qdisc-qfq.network', '11-dummy.netdev')
2624 self
.wait_online(['dummy98:routable', 'test1:routable'])
2626 output
= check_output('tc qdisc show dev dummy98')
2628 self
.assertRegex(output
, 'qdisc drr 2: root')
2629 output
= check_output('tc class show dev dummy98')
2631 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
2633 output
= check_output('tc qdisc show dev test1')
2635 self
.assertRegex(output
, 'qdisc qfq 2: root')
2636 output
= check_output('tc class show dev test1')
2638 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
2639 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
2641 @expectedFailureIfCAKEIsNotAvailable()
2642 def test_qdisc_cake(self
):
2643 copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')
2645 self
.wait_online(['dummy98:routable'])
2647 output
= check_output('tc qdisc show dev dummy98')
2649 self
.assertRegex(output
, 'qdisc cake 3a: root')
2650 self
.assertRegex(output
, 'bandwidth 500Mbit')
2651 self
.assertRegex(output
, 'overhead 128')
2653 @expectedFailureIfPIEIsNotAvailable()
2654 def test_qdisc_pie(self
):
2655 copy_unit_to_networkd_unit_path('25-qdisc-pie.network', '12-dummy.netdev')
2657 self
.wait_online(['dummy98:routable'])
2659 output
= check_output('tc qdisc show dev dummy98')
2661 self
.assertRegex(output
, 'qdisc pie 3a: root')
2662 self
.assertRegex(output
, 'limit 200000')
2664 @expectedFailureIfHHFIsNotAvailable()
2665 def test_qdisc_hhf(self
):
2666 copy_unit_to_networkd_unit_path('25-qdisc-hhf.network', '12-dummy.netdev')
2668 self
.wait_online(['dummy98:routable'])
2670 output
= check_output('tc qdisc show dev dummy98')
2672 self
.assertRegex(output
, 'qdisc hhf 3a: root')
2673 self
.assertRegex(output
, 'limit 1022p')
2675 @expectedFailureIfETSIsNotAvailable()
2676 def test_qdisc_ets(self
):
2677 copy_unit_to_networkd_unit_path('25-qdisc-ets.network', '12-dummy.netdev')
2679 self
.wait_online(['dummy98:routable'])
2681 output
= check_output('tc qdisc show dev dummy98')
2684 self
.assertRegex(output
, 'qdisc ets 3a: root')
2685 self
.assertRegex(output
, 'bands 10 strict 3')
2686 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
2687 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
2689 @expectedFailureIfFQPIEIsNotAvailable()
2690 def test_qdisc_fq_pie(self
):
2691 copy_unit_to_networkd_unit_path('25-qdisc-fq_pie.network', '12-dummy.netdev')
2693 self
.wait_online(['dummy98:routable'])
2695 output
= check_output('tc qdisc show dev dummy98')
2698 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
2699 self
.assertRegex(output
, 'limit 200000p')
2701 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
2702 def test_sriov(self
):
2703 call('rmmod netdevsim', stderr
=subprocess
.DEVNULL
)
2704 call('modprobe netdevsim', stderr
=subprocess
.DEVNULL
)
2705 with
open('/sys/bus/netdevsim/new_device', mode
='w') as f
:
2708 call('udevadm settle')
2709 call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr
=subprocess
.DEVNULL
)
2710 with
open('/sys/class/net/eni99np1/device/sriov_numvfs', mode
='w') as f
:
2713 copy_unit_to_networkd_unit_path('25-sriov.network')
2715 self
.wait_online(['eni99np1:routable'])
2717 output
= check_output('ip link show dev eni99np1')
2719 self
.assertRegex(output
,
2720 '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 *'
2721 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
2722 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
2725 call('rmmod netdevsim', stderr
=subprocess
.DEVNULL
)
2727 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
2734 'state-file-tests.network',
2738 remove_links(self
.links
)
2739 stop_networkd(show_logs
=False)
2742 remove_links(self
.links
)
2743 remove_unit_from_networkd_path(self
.units
)
2744 stop_networkd(show_logs
=True)
2746 def test_state_file(self
):
2747 copy_unit_to_networkd_unit_path('12-dummy.netdev', 'state-file-tests.network')
2749 self
.wait_online(['dummy98:routable'])
2751 output
= check_output(*networkctl_cmd
, '--no-legend', 'list', 'dummy98', env
=env
)
2753 ifindex
= output
.split()[0]
2755 path
= os
.path
.join('/run/systemd/netif/links/', ifindex
)
2756 self
.assertTrue(os
.path
.exists(path
))
2758 # make link state file updated
2759 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
2761 with
open(path
) as f
:
2763 self
.assertRegex(data
, r
'ADMIN_STATE=configured')
2764 self
.assertRegex(data
, r
'OPER_STATE=routable')
2765 self
.assertRegex(data
, r
'REQUIRED_FOR_ONLINE=yes')
2766 self
.assertRegex(data
, r
'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
2767 self
.assertRegex(data
, r
'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
2768 self
.assertRegex(data
, r
'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
2769 self
.assertRegex(data
, r
'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
2770 self
.assertRegex(data
, r
'DOMAINS=hogehoge')
2771 self
.assertRegex(data
, r
'ROUTE_DOMAINS=foofoo')
2772 self
.assertRegex(data
, r
'LLMNR=no')
2773 self
.assertRegex(data
, r
'MDNS=yes')
2774 self
.assertRegex(data
, r
'DNSSEC=no')
2775 self
.assertRegex(data
, r
'ADDRESSES=192.168.(10.10|12.12)/24 192.168.(12.12|10.10)/24')
2777 check_output(*resolvectl_cmd
, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env
=env
)
2778 check_output(*resolvectl_cmd
, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env
=env
)
2779 check_output(*resolvectl_cmd
, 'llmnr', 'dummy98', 'yes', env
=env
)
2780 check_output(*resolvectl_cmd
, 'mdns', 'dummy98', 'no', env
=env
)
2781 check_output(*resolvectl_cmd
, 'dnssec', 'dummy98', 'yes', env
=env
)
2782 check_output(*timedatectl_cmd
, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env
=env
)
2784 with
open(path
) as f
:
2786 self
.assertRegex(data
, r
'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
2787 self
.assertRegex(data
, r
'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
2788 self
.assertRegex(data
, r
'DOMAINS=hogehogehoge')
2789 self
.assertRegex(data
, r
'ROUTE_DOMAINS=foofoofoo')
2790 self
.assertRegex(data
, r
'LLMNR=yes')
2791 self
.assertRegex(data
, r
'MDNS=no')
2792 self
.assertRegex(data
, r
'DNSSEC=yes')
2794 check_output(*timedatectl_cmd
, 'revert', 'dummy98', env
=env
)
2796 with
open(path
) as f
:
2798 self
.assertRegex(data
, r
'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
2799 self
.assertRegex(data
, r
'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
2800 self
.assertRegex(data
, r
'DOMAINS=hogehogehoge')
2801 self
.assertRegex(data
, r
'ROUTE_DOMAINS=foofoofoo')
2802 self
.assertRegex(data
, r
'LLMNR=yes')
2803 self
.assertRegex(data
, r
'MDNS=no')
2804 self
.assertRegex(data
, r
'DNSSEC=yes')
2806 check_output(*resolvectl_cmd
, 'revert', 'dummy98', env
=env
)
2808 with
open(path
) as f
:
2810 self
.assertRegex(data
, r
'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
2811 self
.assertRegex(data
, r
'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
2812 self
.assertRegex(data
, r
'DOMAINS=hogehoge')
2813 self
.assertRegex(data
, r
'ROUTE_DOMAINS=foofoo')
2814 self
.assertRegex(data
, r
'LLMNR=no')
2815 self
.assertRegex(data
, r
'MDNS=yes')
2816 self
.assertRegex(data
, r
'DNSSEC=no')
2818 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
2828 '23-active-slave.network',
2829 '23-bond199.network',
2830 '23-primary-slave.network',
2831 '25-bond-active-backup-slave.netdev',
2834 'bond-slave.network']
2837 remove_links(self
.links
)
2838 stop_networkd(show_logs
=False)
2841 remove_links(self
.links
)
2842 remove_unit_from_networkd_path(self
.units
)
2843 stop_networkd(show_logs
=True)
2845 def test_bond_active_slave(self
):
2846 copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2848 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
2850 output
= check_output('ip -d link show bond199')
2852 self
.assertRegex(output
, 'active_slave dummy98')
2854 def test_bond_primary_slave(self
):
2855 copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
2857 self
.wait_online(['dummy98:enslaved', 'bond199:degraded'])
2859 output
= check_output('ip -d link show bond199')
2861 self
.assertRegex(output
, 'primary dummy98')
2863 def test_bond_operstate(self
):
2864 copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
2865 'bond99.network','bond-slave.network')
2867 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
2869 output
= check_output('ip -d link show dummy98')
2871 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
2873 output
= check_output('ip -d link show test1')
2875 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
2877 output
= check_output('ip -d link show bond99')
2879 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
2881 self
.wait_operstate('dummy98', 'enslaved')
2882 self
.wait_operstate('test1', 'enslaved')
2883 self
.wait_operstate('bond99', 'routable')
2885 check_output('ip link set dummy98 down')
2887 self
.wait_operstate('dummy98', 'off')
2888 self
.wait_operstate('test1', 'enslaved')
2889 self
.wait_operstate('bond99', 'degraded-carrier')
2891 check_output('ip link set dummy98 up')
2893 self
.wait_operstate('dummy98', 'enslaved')
2894 self
.wait_operstate('test1', 'enslaved')
2895 self
.wait_operstate('bond99', 'routable')
2897 check_output('ip link set dummy98 down')
2898 check_output('ip link set test1 down')
2900 self
.wait_operstate('dummy98', 'off')
2901 self
.wait_operstate('test1', 'off')
2903 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
2904 # Huh? Kernel does not recognize that all slave interfaces are down?
2905 # Let's confirm that networkd's operstate is consistent with ip's result.
2906 self
.assertNotRegex(output
, 'NO-CARRIER')
2908 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
2918 '26-bridge-configure-without-carrier.network',
2919 '26-bridge-mdb-master.network',
2920 '26-bridge-mdb-slave.network',
2921 '26-bridge-slave-interface-1.network',
2922 '26-bridge-slave-interface-2.network',
2923 '26-bridge-vlan-master.network',
2924 '26-bridge-vlan-slave.network',
2925 'bridge99-ignore-carrier-loss.network',
2928 routing_policy_rule_tables
= ['100']
2931 remove_routing_policy_rule_tables(self
.routing_policy_rule_tables
)
2932 remove_links(self
.links
)
2933 stop_networkd(show_logs
=False)
2936 remove_routing_policy_rule_tables(self
.routing_policy_rule_tables
)
2937 remove_links(self
.links
)
2938 remove_unit_from_networkd_path(self
.units
)
2939 stop_networkd(show_logs
=True)
2941 def test_bridge_vlan(self
):
2942 copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave.network',
2943 '26-bridge.netdev', '26-bridge-vlan-master.network')
2945 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
2947 output
= check_output('bridge vlan show dev test1')
2949 self
.assertNotRegex(output
, '4063')
2950 for i
in range(4064, 4095):
2951 self
.assertRegex(output
, f
'{i}')
2952 self
.assertNotRegex(output
, '4095')
2954 output
= check_output('bridge vlan show dev bridge99')
2956 self
.assertNotRegex(output
, '4059')
2957 for i
in range(4060, 4095):
2958 self
.assertRegex(output
, f
'{i}')
2959 self
.assertNotRegex(output
, '4095')
2961 def test_bridge_mdb(self
):
2962 copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-mdb-slave.network',
2963 '26-bridge.netdev', '26-bridge-mdb-master.network')
2965 self
.wait_online(['test1:enslaved', 'bridge99:degraded'])
2967 output
= check_output('bridge mdb show dev bridge99')
2969 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
2970 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
2972 def test_bridge_property(self
):
2973 copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
2974 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
2977 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
2979 output
= check_output('ip -d link show test1')
2981 self
.assertRegex(output
, 'master')
2982 self
.assertRegex(output
, 'bridge')
2984 output
= check_output('ip -d link show dummy98')
2986 self
.assertRegex(output
, 'master')
2987 self
.assertRegex(output
, 'bridge')
2989 output
= check_output('ip addr show bridge99')
2991 self
.assertRegex(output
, '192.168.0.15/24')
2993 output
= check_output('bridge -d link show dummy98')
2995 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
2996 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
2997 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
2998 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
2999 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
3000 # CONFIG_BRIDGE_IGMP_SNOOPING=y
3001 if (os
.path
.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
3002 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
3003 if (os
.path
.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress')):
3004 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
3005 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
3006 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'priority'), '23')
3007 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard'), '1')
3008 self
.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'root_block'), '1')
3010 output
= check_output('bridge -d link show test1')
3012 self
.assertEqual(read_bridge_port_attr('bridge99', 'test1', 'priority'), '0')
3014 check_output('ip address add 192.168.0.16/24 dev bridge99')
3017 output
= check_output('ip addr show bridge99')
3019 self
.assertRegex(output
, '192.168.0.16/24')
3022 print('### ip -6 route list table all dev bridge99')
3023 output
= check_output('ip -6 route list table all dev bridge99')
3025 self
.assertRegex(output
, 'ff00::/8 table local metric 256 pref medium')
3027 self
.assertEqual(call('ip link del test1'), 0)
3029 self
.wait_operstate('bridge99', 'degraded-carrier')
3031 check_output('ip link del dummy98')
3033 self
.wait_operstate('bridge99', 'no-carrier')
3035 output
= check_output('ip address show bridge99')
3037 self
.assertRegex(output
, 'NO-CARRIER')
3038 self
.assertNotRegex(output
, '192.168.0.15/24')
3039 self
.assertNotRegex(output
, '192.168.0.16/24')
3041 print('### ip -6 route list table all dev bridge99')
3042 output
= check_output('ip -6 route list table all dev bridge99')
3044 self
.assertRegex(output
, 'ff00::/8 table local metric 256 (linkdown )?pref medium')
3046 def test_bridge_configure_without_carrier(self
):
3047 copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
3051 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
3052 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
3053 with self
.subTest(test
=test
):
3054 if test
== 'no-slave':
3055 # bridge has no slaves; it's up but *might* not have carrier
3056 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
3057 # due to a bug in the kernel, newly-created bridges are brought up
3058 # *with* carrier, unless they have had any setting changed; e.g.
3059 # their mac set, priority set, etc. Then, they will lose carrier
3060 # as soon as a (down) slave interface is added, and regain carrier
3061 # again once the slave interface is brought up.
3062 #self.check_link_attr('bridge99', 'carrier', '0')
3063 elif test
== 'add-slave':
3064 # add slave to bridge, but leave it down; bridge is definitely no-carrier
3065 self
.check_link_attr('test1', 'operstate', 'down')
3066 check_output('ip link set dev test1 master bridge99')
3067 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
3068 self
.check_link_attr('bridge99', 'carrier', '0')
3069 elif test
== 'slave-up':
3070 # bring up slave, which will have carrier; bridge gains carrier
3071 check_output('ip link set dev test1 up')
3072 self
.wait_online(['bridge99:routable'])
3073 self
.check_link_attr('bridge99', 'carrier', '1')
3074 elif test
== 'slave-no-carrier':
3075 # drop slave carrier; bridge loses carrier
3076 check_output('ip link set dev test1 carrier off')
3077 self
.wait_online(['bridge99:no-carrier:no-carrier'])
3078 self
.check_link_attr('bridge99', 'carrier', '0')
3079 elif test
== 'slave-carrier':
3080 # restore slave carrier; bridge gains carrier
3081 check_output('ip link set dev test1 carrier on')
3082 self
.wait_online(['bridge99:routable'])
3083 self
.check_link_attr('bridge99', 'carrier', '1')
3084 elif test
== 'slave-down':
3085 # bring down slave; bridge loses carrier
3086 check_output('ip link set dev test1 down')
3087 self
.wait_online(['bridge99:no-carrier:no-carrier'])
3088 self
.check_link_attr('bridge99', 'carrier', '0')
3090 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'bridge99', env
=env
)
3091 self
.assertRegex(output
, '10.1.2.3')
3092 self
.assertRegex(output
, '10.1.2.1')
3094 def test_bridge_ignore_carrier_loss(self
):
3095 copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
3096 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
3097 'bridge99-ignore-carrier-loss.network')
3099 self
.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
3101 check_output('ip address add 192.168.0.16/24 dev bridge99')
3104 check_output('ip link del test1')
3105 check_output('ip link del dummy98')
3108 output
= check_output('ip address show bridge99')
3110 self
.assertRegex(output
, 'NO-CARRIER')
3111 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
3112 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
3114 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
3115 copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network',
3116 'bridge99-ignore-carrier-loss.network')
3118 self
.wait_online(['bridge99:no-carrier'])
3120 for trial
in range(4):
3121 check_output('ip link add dummy98 type dummy')
3122 check_output('ip link set dummy98 up')
3124 check_output('ip link del dummy98')
3126 self
.wait_online(['bridge99:routable', 'dummy98:enslaved'])
3128 output
= check_output('ip address show bridge99')
3130 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
3132 output
= check_output('ip rule list table 100')
3134 self
.assertEqual(output
, '0: from all to 8.8.8.8 lookup 100')
3136 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
3140 '23-emit-lldp.network',
3145 remove_links(self
.links
)
3146 stop_networkd(show_logs
=False)
3149 remove_links(self
.links
)
3150 remove_unit_from_networkd_path(self
.units
)
3151 stop_networkd(show_logs
=True)
3153 def test_lldp(self
):
3154 copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
3156 self
.wait_online(['veth99:degraded', 'veth-peer:degraded'])
3158 output
= check_output(*networkctl_cmd
, 'lldp', env
=env
)
3160 self
.assertRegex(output
, 'veth-peer')
3161 self
.assertRegex(output
, 'veth99')
3163 class NetworkdRATests(unittest
.TestCase
, Utilities
):
3168 'ipv6-prefix.network',
3169 'ipv6-prefix-veth.network',
3170 'ipv6-prefix-veth-token-static.network',
3171 'ipv6-prefix-veth-token-prefixstable.network',
3172 'ipv6-prefix-veth-token-prefixstable-without-address.network']
3175 remove_links(self
.links
)
3176 stop_networkd(show_logs
=False)
3179 remove_links(self
.links
)
3180 remove_unit_from_networkd_path(self
.units
)
3181 stop_networkd(show_logs
=True)
3183 def test_ipv6_prefix_delegation(self
):
3184 copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
3186 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
3188 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
3190 self
.assertRegex(output
, 'fe80::')
3191 self
.assertRegex(output
, '2002:da8:1::1')
3193 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3195 self
.assertRegex(output
, '2002:da8:1:0')
3197 def test_ipv6_token_static(self
):
3198 copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static.network')
3200 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
3202 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3204 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
3205 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
3206 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
3207 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
3209 def test_ipv6_token_prefixstable(self
):
3210 copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable.network')
3212 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
3214 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3216 self
.assertRegex(output
, '2002:da8:1:0')
3217 self
.assertRegex(output
, '2002:da8:2:0.*78:9abc') # EUI
3219 def test_ipv6_token_prefixstable_without_address(self
):
3220 copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable-without-address.network')
3222 self
.wait_online(['veth99:routable', 'veth-peer:degraded'])
3224 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3226 self
.assertRegex(output
, '2002:da8:1:0')
3227 self
.assertRegex(output
, '2002:da8:2:0')
3229 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
3234 'dhcp-client.network',
3235 'dhcp-client-timezone-router.network',
3236 'dhcp-server.network',
3237 'dhcp-server-timezone-router.network']
3240 remove_links(self
.links
)
3241 stop_networkd(show_logs
=False)
3244 remove_links(self
.links
)
3245 remove_unit_from_networkd_path(self
.units
)
3246 stop_networkd(show_logs
=True)
3248 def test_dhcp_server(self
):
3249 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
3251 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3253 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3255 self
.assertRegex(output
, '192.168.5.*')
3256 self
.assertRegex(output
, 'Gateway: 192.168.5.1')
3257 self
.assertRegex(output
, 'DNS: 192.168.5.1')
3258 self
.assertRegex(output
, 'NTP: 192.168.5.1')
3260 def test_emit_router_timezone(self
):
3261 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
3263 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3265 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3267 self
.assertRegex(output
, 'Gateway: 192.168.5.*')
3268 self
.assertRegex(output
, '192.168.5.*')
3269 self
.assertRegex(output
, 'Europe/Berlin')
3271 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
3280 'dhcp-client-anonymize.network',
3281 'dhcp-client-decline.network',
3282 'dhcp-client-gateway-ipv4.network',
3283 'dhcp-client-gateway-ipv6.network',
3284 'dhcp-client-gateway-onlink-implicit.network',
3285 'dhcp-client-ipv4-dhcp-settings.network',
3286 'dhcp-client-ipv4-only-ipv6-disabled.network',
3287 'dhcp-client-ipv4-only.network',
3288 'dhcp-client-ipv4-use-routes-use-gateway.network',
3289 'dhcp-client-ipv6-only.network',
3290 'dhcp-client-ipv6-rapid-commit.network',
3291 'dhcp-client-keep-configuration-dhcp-on-stop.network',
3292 'dhcp-client-keep-configuration-dhcp.network',
3293 'dhcp-client-listen-port.network',
3294 'dhcp-client-reassign-static-routes-ipv4.network',
3295 'dhcp-client-reassign-static-routes-ipv6.network',
3296 'dhcp-client-route-metric.network',
3297 'dhcp-client-route-table.network',
3298 'dhcp-client-use-dns-ipv4-and-ra.network',
3299 'dhcp-client-use-dns-ipv4.network',
3300 'dhcp-client-use-dns-no.network',
3301 'dhcp-client-use-dns-yes.network',
3302 'dhcp-client-use-domains.network',
3303 'dhcp-client-vrf.network',
3304 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
3305 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
3306 'dhcp-client-with-static-address.network',
3307 'dhcp-client.network',
3308 'dhcp-server-decline.network',
3309 'dhcp-server-veth-peer.network',
3310 'dhcp-v4-server-veth-peer.network',
3314 stop_dnsmasq(dnsmasq_pid_file
)
3315 remove_links(self
.links
)
3316 stop_networkd(show_logs
=False)
3319 stop_dnsmasq(dnsmasq_pid_file
)
3322 remove_links(self
.links
)
3323 remove_unit_from_networkd_path(self
.units
)
3324 stop_networkd(show_logs
=True)
3326 def test_dhcp_client_ipv6_only(self
):
3327 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
3330 self
.wait_online(['veth-peer:carrier'])
3332 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3334 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3336 self
.assertRegex(output
, '2600::')
3337 self
.assertNotRegex(output
, '192.168.5')
3339 output
= check_output('ip addr show dev veth99')
3341 self
.assertRegex(output
, '2600::')
3342 self
.assertNotRegex(output
, '192.168.5')
3343 self
.assertNotRegex(output
, 'tentative')
3345 # Confirm that ipv6 token is not set in the kernel
3346 output
= check_output('ip token show dev veth99')
3348 self
.assertRegex(output
, 'token :: dev veth99')
3350 def test_dhcp_client_ipv4_only(self
):
3351 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only-ipv6-disabled.network')
3354 self
.wait_online(['veth-peer:carrier'])
3355 start_dnsmasq(additional_options
='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time
='2m')
3356 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3358 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3360 self
.assertNotRegex(output
, '2600::')
3361 self
.assertRegex(output
, '192.168.5')
3362 self
.assertRegex(output
, '192.168.5.6')
3363 self
.assertRegex(output
, '192.168.5.7')
3365 # checking routes to DNS servers
3366 output
= check_output('ip route show dev veth99')
3368 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
3369 self
.assertRegex(output
, r
'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
3370 self
.assertRegex(output
, r
'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
3372 stop_dnsmasq(dnsmasq_pid_file
)
3373 start_dnsmasq(additional_options
='--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', lease_time
='2m')
3375 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
3376 print('Wait for the dynamic address to be renewed')
3379 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3381 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3383 self
.assertNotRegex(output
, '2600::')
3384 self
.assertRegex(output
, '192.168.5')
3385 self
.assertNotRegex(output
, '192.168.5.6')
3386 self
.assertRegex(output
, '192.168.5.7')
3387 self
.assertRegex(output
, '192.168.5.8')
3389 # checking routes to DNS servers
3390 output
= check_output('ip route show dev veth99')
3392 self
.assertNotRegex(output
, r
'192.168.5.6')
3393 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
3394 self
.assertRegex(output
, r
'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
3395 self
.assertRegex(output
, r
'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024')
3397 def test_dhcp_client_ipv4_use_routes_gateway(self
):
3398 for (routes
, gateway
, dnsroutes
) in itertools
.product([True, False, None], repeat
=3):
3400 with self
.subTest(routes
=routes
, gateway
=gateway
, dnsroutes
=dnsroutes
):
3401 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dnsroutes
)
3404 def _test_dhcp_client_ipv4_use_routes_gateway(self
, routes
, gateway
, dnsroutes
):
3405 testunit
= 'dhcp-client-ipv4-use-routes-use-gateway.network'
3406 testunits
= ['25-veth.netdev', 'dhcp-server-veth-peer.network', testunit
]
3408 testunits
.append(f
'{testunit}.d/use-routes-{routes}.conf');
3410 testunits
.append(f
'{testunit}.d/use-gateway-{gateway}.conf');
3411 if dnsroutes
!= None:
3412 testunits
.append(f
'{testunit}.d/use-dns-routes-{dnsroutes}.conf');
3413 copy_unit_to_networkd_unit_path(*testunits
, dropins
=False)
3416 self
.wait_online(['veth-peer:carrier'])
3417 start_dnsmasq(additional_options
='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time
='2m')
3418 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3420 output
= check_output('ip route show dev veth99')
3423 # UseRoutes= defaults to true
3424 useroutes
= routes
in [True, None]
3425 # UseGateway= defaults to useroutes
3426 usegateway
= useroutes
if gateway
== None else gateway
3430 self
.assertRegex(output
, r
'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.181 metric 1024')
3432 self
.assertNotRegex(output
, r
'192.168.5.5')
3436 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
3438 self
.assertNotRegex(output
, r
'default via 192.168.5.1')
3440 # Check RoutesToDNS=, which defaults to false
3442 self
.assertRegex(output
, r
'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
3443 self
.assertRegex(output
, r
'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
3445 self
.assertNotRegex(output
, r
'192.168.5.6')
3446 self
.assertNotRegex(output
, r
'192.168.5.7')
3448 def test_dhcp_client_ipv4_ipv6(self
):
3449 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
3450 'dhcp-client-ipv4-only.network')
3452 self
.wait_online(['veth-peer:carrier'])
3454 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3456 # link become 'routable' when at least one protocol provide an valid address.
3457 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
3458 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
3460 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3462 self
.assertRegex(output
, '2600::')
3463 self
.assertRegex(output
, '192.168.5')
3465 def test_dhcp_client_settings(self
):
3466 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network')
3469 self
.wait_online(['veth-peer:carrier'])
3471 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3473 print('## ip address show dev veth99')
3474 output
= check_output('ip address show dev veth99')
3476 self
.assertRegex(output
, '12:34:56:78:9a:bc')
3477 self
.assertRegex(output
, '192.168.5')
3478 self
.assertRegex(output
, '1492')
3480 print('## ip route show table main dev veth99')
3481 output
= check_output('ip route show table main dev veth99')
3484 main_table_is_empty
= output
== ''
3485 if not main_table_is_empty
:
3486 self
.assertNotRegex(output
, 'proto dhcp')
3488 print('## ip route show table 211 dev veth99')
3489 output
= check_output('ip route show table 211 dev veth99')
3491 self
.assertRegex(output
, 'default via 192.168.5.1 proto dhcp')
3492 if main_table_is_empty
:
3493 self
.assertRegex(output
, '192.168.5.0/24 proto dhcp')
3494 self
.assertRegex(output
, '192.168.5.0/24 via 192.168.5.5 proto dhcp')
3495 self
.assertRegex(output
, '192.168.5.1 proto dhcp scope link')
3497 print('## dnsmasq log')
3498 self
.assertTrue(search_words_in_dnsmasq_log('vendor class: SusantVendorTest', True))
3499 self
.assertTrue(search_words_in_dnsmasq_log('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc'))
3500 self
.assertTrue(search_words_in_dnsmasq_log('client provides name: test-hostname'))
3501 self
.assertTrue(search_words_in_dnsmasq_log('26:mtu'))
3503 def test_dhcp6_client_settings_rapidcommit_true(self
):
3504 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
3506 self
.wait_online(['veth-peer:carrier'])
3508 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3510 output
= check_output('ip address show dev veth99')
3512 self
.assertRegex(output
, '12:34:56:78:9a:bc')
3513 self
.assertTrue(search_words_in_dnsmasq_log('14:rapid-commit', True))
3515 def test_dhcp6_client_settings_rapidcommit_false(self
):
3516 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
3518 self
.wait_online(['veth-peer:carrier'])
3520 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3522 output
= check_output('ip address show dev veth99')
3524 self
.assertRegex(output
, '12:34:56:78:9a:bc')
3525 self
.assertFalse(search_words_in_dnsmasq_log('14:rapid-commit', True))
3527 def test_dhcp_client_settings_anonymize(self
):
3528 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
3530 self
.wait_online(['veth-peer:carrier'])
3532 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3534 self
.assertFalse(search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True))
3535 self
.assertFalse(search_words_in_dnsmasq_log('test-hostname'))
3536 self
.assertFalse(search_words_in_dnsmasq_log('26:mtu'))
3538 def test_dhcp_client_listen_port(self
):
3539 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network')
3541 self
.wait_online(['veth-peer:carrier'])
3542 start_dnsmasq('--dhcp-alternate-port=67,5555')
3543 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3545 output
= check_output('ip -4 address show dev veth99')
3547 self
.assertRegex(output
, '192.168.5.* dynamic')
3549 def test_dhcp_client_with_static_address(self
):
3550 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network',
3551 'dhcp-client-with-static-address.network')
3553 self
.wait_online(['veth-peer:carrier'])
3555 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3557 output
= check_output('ip address show dev veth99 scope global')
3559 self
.assertRegex(output
, r
'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99')
3560 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global secondary dynamic veth99')
3562 output
= check_output('ip route show dev veth99')
3564 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
3565 self
.assertRegex(output
, r
'192.168.5.0/24 proto kernel scope link src 192.168.5.250')
3566 self
.assertRegex(output
, r
'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
3567 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
3569 def test_dhcp_route_table_id(self
):
3570 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network')
3572 self
.wait_online(['veth-peer:carrier'])
3574 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3576 output
= check_output('ip route show table 12')
3578 self
.assertRegex(output
, 'veth99 proto dhcp')
3579 self
.assertRegex(output
, '192.168.5.1')
3581 def test_dhcp_route_metric(self
):
3582 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network')
3584 self
.wait_online(['veth-peer:carrier'])
3586 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3588 output
= check_output('ip route show dev veth99')
3590 self
.assertRegex(output
, 'metric 24')
3592 def test_dhcp_client_reassign_static_routes_ipv4(self
):
3593 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3594 'dhcp-client-reassign-static-routes-ipv4.network')
3596 self
.wait_online(['veth-peer:carrier'])
3597 start_dnsmasq(lease_time
='2m')
3598 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3600 output
= check_output('ip address show dev veth99 scope global')
3602 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3604 output
= check_output('ip route show dev veth99')
3606 self
.assertRegex(output
, r
'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
3607 self
.assertRegex(output
, r
'192.168.5.0/24 proto static')
3608 self
.assertRegex(output
, r
'192.168.6.0/24 proto static')
3609 self
.assertRegex(output
, r
'192.168.7.0/24 proto static')
3611 stop_dnsmasq(dnsmasq_pid_file
)
3612 start_dnsmasq(ipv4_range
='192.168.5.210,192.168.5.220', lease_time
='2m')
3614 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
3615 print('Wait for the dynamic address to be renewed')
3618 self
.wait_online(['veth99:routable'])
3620 output
= check_output('ip route show dev veth99')
3622 self
.assertRegex(output
, r
'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
3623 self
.assertRegex(output
, r
'192.168.5.0/24 proto static')
3624 self
.assertRegex(output
, r
'192.168.6.0/24 proto static')
3625 self
.assertRegex(output
, r
'192.168.7.0/24 proto static')
3627 def test_dhcp_client_reassign_static_routes_ipv6(self
):
3628 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3629 'dhcp-client-reassign-static-routes-ipv6.network')
3631 self
.wait_online(['veth-peer:carrier'])
3632 start_dnsmasq(lease_time
='2m')
3633 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3635 output
= check_output('ip address show dev veth99 scope global')
3637 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f]*/128 scope global (noprefixroute dynamic|dynamic noprefixroute)')
3639 output
= check_output('ip -6 route show dev veth99')
3641 self
.assertRegex(output
, r
'2600::/64 proto ra metric 1024')
3642 self
.assertRegex(output
, r
'2600:0:0:1::/64 proto static metric 1024 pref medium')
3644 stop_dnsmasq(dnsmasq_pid_file
)
3645 start_dnsmasq(ipv6_range
='2600::30,2600::40', lease_time
='2m')
3647 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
3648 print('Wait for the dynamic address to be renewed')
3651 self
.wait_online(['veth99:routable'])
3653 output
= check_output('ip -6 route show dev veth99')
3655 self
.assertRegex(output
, r
'2600::/64 proto ra metric 1024')
3656 self
.assertRegex(output
, r
'2600:0:0:1::/64 proto static metric 1024 pref medium')
3658 def test_dhcp_keep_configuration_dhcp(self
):
3659 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-keep-configuration-dhcp.network')
3661 self
.wait_online(['veth-peer:carrier'])
3662 start_dnsmasq(lease_time
='2m')
3663 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3665 output
= check_output('ip address show dev veth99 scope global')
3667 self
.assertRegex(output
, r
'192.168.5.*')
3669 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3671 self
.assertRegex(output
, r
'192.168.5.*')
3673 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
3674 stop_dnsmasq(dnsmasq_pid_file
)
3676 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
3677 print('Wait for the dynamic address to be expired')
3680 print('The lease address should be kept after lease expired')
3681 output
= check_output('ip address show dev veth99 scope global')
3683 self
.assertRegex(output
, r
'192.168.5.*')
3685 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3687 self
.assertRegex(output
, r
'192.168.5.*')
3689 check_output('systemctl stop systemd-networkd.socket')
3690 check_output('systemctl stop systemd-networkd.service')
3692 print('The lease address should be kept after networkd stopped')
3693 output
= check_output('ip address show dev veth99 scope global')
3695 self
.assertRegex(output
, r
'192.168.5.*')
3697 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3699 self
.assertRegex(output
, r
'192.168.5.*')
3702 self
.wait_online(['veth-peer:routable'])
3704 print('Still the lease address should be kept after networkd restarted')
3705 output
= check_output('ip address show dev veth99 scope global')
3707 self
.assertRegex(output
, r
'192.168.5.*')
3709 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3711 self
.assertRegex(output
, r
'192.168.5.*')
3713 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
3714 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-keep-configuration-dhcp-on-stop.network')
3716 self
.wait_online(['veth-peer:carrier'])
3717 start_dnsmasq(lease_time
='2m')
3718 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3720 output
= check_output('ip address show dev veth99 scope global')
3722 self
.assertRegex(output
, r
'192.168.5.*')
3724 stop_dnsmasq(dnsmasq_pid_file
)
3725 check_output('systemctl stop systemd-networkd.socket')
3726 check_output('systemctl stop systemd-networkd.service')
3728 output
= check_output('ip address show dev veth99 scope global')
3730 self
.assertRegex(output
, r
'192.168.5.*')
3733 self
.wait_online(['veth-peer:routable'])
3735 output
= check_output('ip address show dev veth99 scope global')
3737 self
.assertNotRegex(output
, r
'192.168.5.*')
3739 def test_dhcp_client_reuse_address_as_static(self
):
3740 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client.network')
3742 self
.wait_online(['veth-peer:carrier'])
3744 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3746 # link become 'routable' when at least one protocol provide an valid address.
3747 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
3748 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
3750 output
= check_output('ip address show dev veth99 scope global')
3752 self
.assertRegex(output
, '192.168.5')
3753 self
.assertRegex(output
, '2600::')
3755 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
)
3756 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
)
3757 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
.group(), 'Address=' + ipv6_address
.group()])
3758 print(static_network
)
3760 remove_unit_from_networkd_path(['dhcp-client.network'])
3762 with
open(os
.path
.join(network_unit_file_path
, 'static.network'), mode
='w') as f
:
3763 f
.write(static_network
)
3765 # When networkd started, the links are already configured, so let's wait for 5 seconds
3766 # the links to be re-configured.
3768 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3770 output
= check_output('ip -4 address show dev veth99 scope global')
3772 self
.assertRegex(output
, '192.168.5')
3773 self
.assertRegex(output
, 'valid_lft forever preferred_lft forever')
3775 output
= check_output('ip -6 address show dev veth99 scope global')
3777 self
.assertRegex(output
, '2600::')
3778 self
.assertRegex(output
, 'valid_lft forever preferred_lft forever')
3780 @expectedFailureIfModuleIsNotAvailable('vrf')
3781 def test_dhcp_client_vrf(self
):
3782 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-vrf.network',
3783 '25-vrf.netdev', '25-vrf.network')
3785 self
.wait_online(['veth-peer:carrier'])
3787 self
.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
3789 # link become 'routable' when at least one protocol provide an valid address.
3790 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
3791 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
3793 print('## ip -d link show dev vrf99')
3794 output
= check_output('ip -d link show dev vrf99')
3796 self
.assertRegex(output
, 'vrf table 42')
3798 print('## ip address show vrf vrf99')
3799 output
= check_output('ip address show vrf vrf99')
3801 self
.assertRegex(output
, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
3802 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3803 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
3804 self
.assertRegex(output
, 'inet6 .* scope link')
3806 print('## ip address show dev veth99')
3807 output
= check_output('ip address show dev veth99')
3809 self
.assertRegex(output
, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
3810 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3811 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
3812 self
.assertRegex(output
, 'inet6 .* scope link')
3814 print('## ip route show vrf vrf99')
3815 output
= check_output('ip route show vrf vrf99')
3817 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
3818 self
.assertRegex(output
, '169.254.0.0/16 dev veth99 proto kernel scope link src 169.254')
3819 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
3820 self
.assertRegex(output
, '192.168.5.0/24 via 192.168.5.5 dev veth99 proto dhcp')
3821 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
3823 print('## ip route show table main dev veth99')
3824 output
= check_output('ip route show table main dev veth99')
3826 self
.assertEqual(output
, '')
3828 def test_dhcp_client_gateway_ipv4(self
):
3829 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3830 'dhcp-client-gateway-ipv4.network')
3832 self
.wait_online(['veth-peer:carrier'])
3834 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3836 output
= check_output('ip route list dev veth99 10.0.0.0/8')
3838 self
.assertRegex(output
, '10.0.0.0/8 via 192.168.5.1 proto static')
3840 def test_dhcp_client_gateway_ipv6(self
):
3841 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3842 'dhcp-client-gateway-ipv6.network')
3844 self
.wait_online(['veth-peer:carrier'])
3846 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3848 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
3850 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
3852 def test_dhcp_client_gateway_onlink_implicit(self
):
3853 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3854 'dhcp-client-gateway-onlink-implicit.network')
3856 self
.wait_online(['veth-peer:carrier'])
3858 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3860 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
3862 self
.assertRegex(output
, '192.168.5')
3864 output
= check_output('ip route list dev veth99 10.0.0.0/8')
3866 self
.assertRegex(output
, 'onlink')
3867 output
= check_output('ip route list dev veth99 192.168.100.0/24')
3869 self
.assertRegex(output
, 'onlink')
3871 def test_dhcp_client_with_ipv4ll_fallback_with_dhcp_server(self
):
3872 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3873 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network')
3875 self
.wait_online(['veth-peer:carrier'])
3876 start_dnsmasq(lease_time
='2m')
3877 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3879 output
= check_output('ip address show dev veth99')
3882 output
= check_output('ip -6 address show dev veth99 scope global dynamic')
3883 self
.assertNotRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
3884 output
= check_output('ip -6 address show dev veth99 scope link')
3885 self
.assertRegex(output
, 'inet6 .* scope link')
3886 output
= check_output('ip -4 address show dev veth99 scope global dynamic')
3887 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3888 output
= check_output('ip -4 address show dev veth99 scope link')
3889 self
.assertNotRegex(output
, 'inet .* scope link')
3891 print('Wait for the dynamic address to be expired')
3894 output
= check_output('ip address show dev veth99')
3897 output
= check_output('ip -6 address show dev veth99 scope global dynamic')
3898 self
.assertNotRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
3899 output
= check_output('ip -6 address show dev veth99 scope link')
3900 self
.assertRegex(output
, 'inet6 .* scope link')
3901 output
= check_output('ip -4 address show dev veth99 scope global dynamic')
3902 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3903 output
= check_output('ip -4 address show dev veth99 scope link')
3904 self
.assertNotRegex(output
, 'inet .* scope link')
3906 search_words_in_dnsmasq_log('DHCPOFFER', show_all
=True)
3908 def test_dhcp_client_with_ipv4ll_fallback_without_dhcp_server(self
):
3909 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3910 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network')
3912 self
.wait_online(['veth99:degraded', 'veth-peer:routable'])
3914 output
= check_output('ip address show dev veth99')
3917 output
= check_output('ip -6 address show dev veth99 scope global dynamic')
3918 self
.assertNotRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
3919 output
= check_output('ip -6 address show dev veth99 scope link')
3920 self
.assertRegex(output
, 'inet6 .* scope link')
3921 output
= check_output('ip -4 address show dev veth99 scope global dynamic')
3922 self
.assertNotRegex(output
, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3923 output
= check_output('ip -4 address show dev veth99 scope link')
3924 self
.assertRegex(output
, 'inet .* scope link')
3926 def test_dhcp_client_route_remove_on_renew(self
):
3927 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
3928 'dhcp-client-ipv4-only-ipv6-disabled.network')
3930 self
.wait_online(['veth-peer:carrier'])
3931 start_dnsmasq(ipv4_range
='192.168.5.100,192.168.5.199', lease_time
='2m')
3932 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3934 # test for issue #12490
3936 output
= check_output('ip -4 address show dev veth99 scope global dynamic')
3938 self
.assertRegex(output
, 'inet 192.168.5.1[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3940 for line
in output
.splitlines():
3941 if 'brd 192.168.5.255 scope global dynamic veth99' in line
:
3942 address1
= line
.split()[1].split('/')[0]
3945 output
= check_output('ip -4 route show dev veth99')
3947 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
3948 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
3950 stop_dnsmasq(dnsmasq_pid_file
)
3951 start_dnsmasq(ipv4_range
='192.168.5.200,192.168.5.250', lease_time
='2m')
3953 print('Wait for the dynamic address to be expired')
3956 output
= check_output('ip -4 address show dev veth99 scope global dynamic')
3958 self
.assertRegex(output
, 'inet 192.168.5.2[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
3960 for line
in output
.splitlines():
3961 if 'brd 192.168.5.255 scope global dynamic veth99' in line
:
3962 address2
= line
.split()[1].split('/')[0]
3965 self
.assertNotEqual(address1
, address2
)
3967 output
= check_output('ip -4 route show dev veth99')
3969 self
.assertNotRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
3970 self
.assertNotRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
3971 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 1024')
3972 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 1024')
3974 def test_dhcp_client_use_dns_yes(self
):
3975 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-yes.network')
3978 self
.wait_online(['veth-peer:carrier'])
3979 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
3980 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
3982 # link become 'routable' when at least one protocol provide an valid address.
3983 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
3984 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
3987 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
3989 self
.assertRegex(output
, '192.168.5.1')
3990 self
.assertRegex(output
, '2600::1')
3992 def test_dhcp_client_use_dns_no(self
):
3993 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-no.network')
3996 self
.wait_online(['veth-peer:carrier'])
3997 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
3998 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4000 # link become 'routable' when at least one protocol provide an valid address.
4001 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
4002 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
4005 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4007 self
.assertNotRegex(output
, '192.168.5.1')
4008 self
.assertNotRegex(output
, '2600::1')
4010 def test_dhcp_client_use_dns_ipv4(self
):
4011 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4.network')
4014 self
.wait_online(['veth-peer:carrier'])
4015 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
4016 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4018 # link become 'routable' when at least one protocol provide an valid address.
4019 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
4020 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
4023 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4025 self
.assertRegex(output
, '192.168.5.1')
4026 self
.assertNotRegex(output
, '2600::1')
4028 def test_dhcp_client_use_dns_ipv4_and_ra(self
):
4029 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4-and-ra.network')
4032 self
.wait_online(['veth-peer:carrier'])
4033 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
4034 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4036 # link become 'routable' when at least one protocol provide an valid address.
4037 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv
='-4')
4038 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
4041 output
= check_output(*resolvectl_cmd
, 'dns', 'veth99', env
=env
)
4043 self
.assertRegex(output
, '192.168.5.1')
4044 self
.assertRegex(output
, '2600::1')
4046 def test_dhcp_client_use_domains(self
):
4047 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-domains.network')
4050 self
.wait_online(['veth-peer:carrier'])
4051 start_dnsmasq('--dhcp-option=option:domain-search,example.com')
4052 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4054 output
= check_output(*networkctl_cmd
, '-n', '0', 'status', 'veth99', env
=env
)
4056 self
.assertRegex(output
, 'Search Domains: example.com')
4059 output
= check_output(*resolvectl_cmd
, 'domain', 'veth99', env
=env
)
4061 self
.assertRegex(output
, 'example.com')
4063 def test_dhcp_client_decline(self
):
4064 copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-decline.network', 'dhcp-client-decline.network')
4067 self
.wait_online(['veth-peer:carrier'])
4068 rc
= call(*wait_online_cmd
, '--timeout=10s', '--interface=veth99:routable', env
=env
)
4069 self
.assertTrue(rc
== 1)
4071 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
4076 'ipv6ra-prefix-client.network',
4077 'ipv6ra-prefix.network'
4081 remove_links(self
.links
)
4082 stop_networkd(show_logs
=False)
4086 remove_links(self
.links
)
4087 remove_unit_from_networkd_path(self
.units
)
4088 stop_networkd(show_logs
=True)
4090 def test_ipv6_route_prefix(self
):
4091 copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network')
4094 self
.wait_online(['veth99:routable', 'veth-peer:routable'])
4096 output
= check_output('ip -6 route show dev veth-peer')
4098 self
.assertRegex(output
, '2001:db8:0:1::/64 proto ra')
4100 output
= check_output('ip addr show dev veth99')
4102 self
.assertNotRegex(output
, '2001:db8:0:1')
4103 self
.assertRegex(output
, '2001:db8:0:2')
4105 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
4110 '12-dummy-mtu.netdev',
4111 '12-dummy-mtu.link',
4116 remove_links(self
.links
)
4117 stop_networkd(show_logs
=False)
4121 remove_links(self
.links
)
4122 remove_unit_from_networkd_path(self
.units
)
4123 stop_networkd(show_logs
=True)
4125 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
4131 self
.wait_online(['dummy98:routable'])
4132 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu
)
4133 self
.assertEqual(read_link_attr('dummy98', 'mtu'), mtu
)
4135 # test normal restart
4137 self
.wait_online(['dummy98:routable'])
4138 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu
)
4139 self
.assertEqual(read_link_attr('dummy98', 'mtu'), mtu
)
4142 self
.reset_check_mtu(mtu
, ipv6_mtu
)
4144 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
4145 ''' test setting mtu/ipv6_mtu with interface already up '''
4148 # note - changing the device mtu resets the ipv6 mtu
4149 run('ip link set up mtu 1501 dev dummy98')
4150 run('ip link set up mtu 1500 dev dummy98')
4151 self
.assertEqual(read_link_attr('dummy98', 'mtu'), '1500')
4152 self
.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), '1500')
4154 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
4156 def test_mtu_network(self
):
4157 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
4158 self
.check_mtu('1600')
4160 def test_mtu_netdev(self
):
4161 copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network', dropins
=False)
4162 # note - MTU set by .netdev happens ONLY at device creation!
4163 self
.check_mtu('1600', reset
=False)
4165 def test_mtu_link(self
):
4166 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', dropins
=False)
4167 # must reload udev because it only picks up new files after 3 second delay
4168 call('udevadm control --reload')
4169 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
4170 self
.check_mtu('1600', reset
=False)
4172 def test_ipv6_mtu(self
):
4173 ''' set ipv6 mtu without setting device mtu '''
4174 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
4175 self
.check_mtu('1500', '1400')
4177 def test_ipv6_mtu_toolarge(self
):
4178 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
4179 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
4180 self
.check_mtu('1500', '1500')
4182 def test_mtu_network_ipv6_mtu(self
):
4183 ''' set ipv6 mtu and set device mtu via network file '''
4184 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
4185 self
.check_mtu('1600', '1550')
4187 def test_mtu_netdev_ipv6_mtu(self
):
4188 ''' set ipv6 mtu and set device mtu via netdev file '''
4189 copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
4190 self
.check_mtu('1600', '1550', reset
=False)
4192 def test_mtu_link_ipv6_mtu(self
):
4193 ''' set ipv6 mtu and set device mtu via link file '''
4194 copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
4195 # must reload udev because it only picks up new files after 3 second delay
4196 call('udevadm control --reload')
4197 self
.check_mtu('1600', '1550', reset
=False)
4200 if __name__
== '__main__':
4201 parser
= argparse
.ArgumentParser()
4202 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
4203 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
4204 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
4205 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
4206 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
4207 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
4208 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
4209 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
4210 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
4211 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
4212 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
4213 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
4214 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
4215 ns
, args
= parser
.parse_known_args(namespace
=unittest
)
4218 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
:
4219 print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
4220 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
4221 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
4222 udevd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-udevd')
4223 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
4224 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
4225 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
4226 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
4229 networkd_bin
= ns
.networkd_bin
4231 resolved_bin
= ns
.resolved_bin
4233 udevd_bin
= ns
.udevd_bin
4234 if ns
.wait_online_bin
:
4235 wait_online_bin
= ns
.wait_online_bin
4236 if ns
.networkctl_bin
:
4237 networkctl_bin
= ns
.networkctl_bin
4238 if ns
.resolvectl_bin
:
4239 resolvectl_bin
= ns
.resolvectl_bin
4240 if ns
.timedatectl_bin
:
4241 timedatectl_bin
= ns
.timedatectl_bin
4243 use_valgrind
= ns
.use_valgrind
4244 enable_debug
= ns
.enable_debug
4245 asan_options
= ns
.asan_options
4246 lsan_options
= ns
.lsan_options
4247 ubsan_options
= ns
.ubsan_options
4250 networkctl_cmd
= ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin
]
4251 resolvectl_cmd
= ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin
]
4252 timedatectl_cmd
= ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin
]
4253 wait_online_cmd
= ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin
]
4255 networkctl_cmd
= [networkctl_bin
]
4256 resolvectl_cmd
= [resolvectl_bin
]
4257 timedatectl_cmd
= [timedatectl_bin
]
4258 wait_online_cmd
= [wait_online_bin
]
4261 env
.update({ 'SYSTEMD_LOG_LEVEL' : 'debug' })
4263 env
.update({ 'ASAN_OPTIONS' : asan_options
})
4265 env
.update({ 'LSAN_OPTIONS' : lsan_options
})
4267 env
.update({ 'UBSAN_OPTIONS' : ubsan_options
})
4270 unittest
.main(testRunner
=unittest
.TextTestRunner(stream
=sys
.stdout
,