Add a helper to the Site() class to wait for an address with specified characteristics to be ready on an interface. In particular this is useful for waiting for IPv6 SLAAC & DAD (Duplicate Address Detection) to complete. Because DAD is not going to be useful in many of our scenarios, also extend Site.ifup() to allow DAD to be switched to optimistic mode or disabled. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- test/tasst/exesite.py | 22 ++++++++++++++++++- test/tasst/meta/static_ifup.py | 40 ++++++++++++++++++++++++++++++++++ test/tasst/meta/veth.py | 26 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/tasst/meta/static_ifup.py diff --git a/test/tasst/exesite.py b/test/tasst/exesite.py index 63872c34..423fccbd 100644 --- a/test/tasst/exesite.py +++ b/test/tasst/exesite.py @@ -102,8 +102,22 @@ class Site(contextlib.AbstractContextManager): info = json.loads(self.output('ip -j link show')) return [i['ifname'] for i in info] - def ifup(self, ifname): + def ifup(self, ifname, *addrs, dad=None): self.require_cmds('ip') + if dad == 'disable': + self.fg(f'sysctl net.ipv6.conf.{ifname}.accept_dad=0', sudo=True) + elif dad == 'optimistic': + self.fg(f'sysctl net.ipv6.conf.{ifname}.optimistic_dad=1', + sudo=True) + elif dad is not None: + raise ValueError + + for a in addrs: + if not isinstance(a, ipaddress.IPv4Interface) \ + and not isinstance(a, ipaddress.IPv6Interface): + raise TypeError + self.fg(f'ip addr add {a.with_prefixlen} dev {ifname}', sudo=True) + self.fg(f'ip link set {ifname} up', sudo=True) def addrinfos(self, ifname, **criteria): @@ -129,6 +143,12 @@ class Site(contextlib.AbstractContextManager): (info,) = json.loads(self.output(f'ip -j link show {ifname}')) return info['mtu'] + def addr_wait(self, ifname, **criteria): + while True: + addrs = self.addrs(ifname, **criteria) + if addrs: + return addrs + def test_site(sitefn): def test_true(s): diff --git a/test/tasst/meta/static_ifup.py b/test/tasst/meta/static_ifup.py new file mode 100644 index 00000000..0896c747 --- /dev/null +++ b/test/tasst/meta/static_ifup.py @@ -0,0 +1,40 @@ +#! /usr/bin/env avocado-runner-avocado-classless + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +""" +Test A Simple Socket Transport + +meta/static_ifup - Static address configuration +""" + +import contextlib +import ipaddress + +from avocado_classless.test import assert_eq_unordered, test + +from tasst.nstool import unshare_site + + +IFNAME = 'testveth' +IFNAME_PEER = 'vethpeer' +TEST_IPS = [ipaddress.ip_interface('192.0.2.1/24'), + ipaddress.ip_interface('2001:db8:9a55::1/112'), + ipaddress.ip_interface('10.1.2.3/8')] + + +(a)contextlib.contextmanager +def setup_ns(): + with unshare_site('ns', '-Un') as ns: + ns.veth(IFNAME, IFNAME_PEER) + ns.ifup(IFNAME, *TEST_IPS, dad='disable') + yield ns + + +@test +def test_addr(): + with setup_ns() as ns: + assert_eq_unordered(ns.addrs(IFNAME, scope='global'), TEST_IPS) diff --git a/test/tasst/meta/veth.py b/test/tasst/meta/veth.py index 053bd9c8..cedcf059 100644 --- a/test/tasst/meta/veth.py +++ b/test/tasst/meta/veth.py @@ -12,6 +12,7 @@ meta/veth.py - Test various veth configurations """ import contextlib +import ipaddress from avocado_classless.test import assert_eq, assert_eq_unordered, test @@ -38,3 +39,28 @@ def test_mtu(): with unconfigured_veth() as (ns1, ns2): assert_eq(ns1.mtu('veth1'), 1500) assert_eq(ns2.mtu('veth2'), 1500) + + +@test +def test_slaac(dad=None): + TESTMAC = '02:aa:bb:cc:dd:ee' + TESTIP = ipaddress.ip_interface('fe80::aa:bbff:fecc:ddee/64') + + with unconfigured_veth() as (ns1, ns2): + ns1.fg(f'ip link set dev veth1 address {TESTMAC}', sudo=True) + + ns1.ifup('veth1', dad=dad) + ns2.ifup('veth2') + + addrs = ns1.addr_wait('veth1', family='inet6', scope='link') + assert_eq(addrs, [TESTIP]) + + +@test +def test_optimistic_dad(): + test_slaac(dad='optimistic') + + +@test +def test_no_dad(): + test_slaac(dad='disable') -- 2.41.0