Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'selftests-drv-net-add-a-simple-tso-test'

Jakub Kicinski says:

====================
selftests: drv-net: add a simple TSO test

Add a simple test for exercising TSO over tunnels.

Similarly to csum test we want to iterate over ip versions.
Rework how addresses are stored in env to make this easier.
====================

Link: https://patch.msgid.link/20250218225426.77726-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+305 -61
+1
tools/testing/selftests/drivers/net/hw/Makefile
··· 15 15 nic_performance.py \ 16 16 pp_alloc_fail.py \ 17 17 rss_ctx.py \ 18 + tso.py \ 18 19 # 19 20 20 21 TEST_FILES := \
+18 -30
tools/testing/selftests/drivers/net/hw/csum.py
··· 9 9 from lib.py import EthtoolFamily, NetDrvEpEnv 10 10 from lib.py import bkg, cmd, wait_port_listen 11 11 12 - def test_receive(cfg, ipv4=False, extra_args=None): 12 + def test_receive(cfg, ipver="6", extra_args=None): 13 13 """Test local nic checksum receive. Remote host sends crafted packets.""" 14 14 if not cfg.have_rx_csum: 15 15 raise KsftSkipEx(f"Test requires rx checksum offload on {cfg.ifname}") 16 16 17 - if ipv4: 18 - ip_args = f"-4 -S {cfg.remote_v4} -D {cfg.v4}" 19 - else: 20 - ip_args = f"-6 -S {cfg.remote_v6} -D {cfg.v6}" 17 + ip_args = f"-{ipver} -S {cfg.remote_addr_v[ipver]} -D {cfg.addr_v[ipver]}" 21 18 22 19 rx_cmd = f"{cfg.bin_local} -i {cfg.ifname} -n 100 {ip_args} -r 1 -R {extra_args}" 23 20 tx_cmd = f"{cfg.bin_remote} -i {cfg.ifname} -n 100 {ip_args} -r 1 -T {extra_args}" ··· 24 27 cmd(tx_cmd, host=cfg.remote) 25 28 26 29 27 - def test_transmit(cfg, ipv4=False, extra_args=None): 30 + def test_transmit(cfg, ipver="6", extra_args=None): 28 31 """Test local nic checksum transmit. Remote host verifies packets.""" 29 32 if (not cfg.have_tx_csum_generic and 30 - not (cfg.have_tx_csum_ipv4 and ipv4) and 31 - not (cfg.have_tx_csum_ipv6 and not ipv4)): 33 + not (cfg.have_tx_csum_ipv4 and ipver == "4") and 34 + not (cfg.have_tx_csum_ipv6 and ipver == "6")): 32 35 raise KsftSkipEx(f"Test requires tx checksum offload on {cfg.ifname}") 33 36 34 - if ipv4: 35 - ip_args = f"-4 -S {cfg.v4} -D {cfg.remote_v4}" 36 - else: 37 - ip_args = f"-6 -S {cfg.v6} -D {cfg.remote_v6}" 37 + ip_args = f"-{ipver} -S {cfg.addr_v[ipver]} -D {cfg.remote_addr_v[ipver]}" 38 38 39 39 # Cannot randomize input when calculating zero checksum 40 40 if extra_args != "-U -Z": ··· 45 51 cmd(tx_cmd) 46 52 47 53 48 - def test_builder(name, cfg, ipv4=False, tx=False, extra_args=""): 54 + def test_builder(name, cfg, ipver="6", tx=False, extra_args=""): 49 55 """Construct specific tests from the common template. 50 56 51 57 Most tests follow the same basic pattern, differing only in 52 58 Direction of the test and optional flags passed to csum.""" 53 59 def f(cfg): 54 - if ipv4: 55 - cfg.require_v4() 56 - else: 57 - cfg.require_v6() 60 + cfg.require_ipver(ipver) 58 61 59 62 if tx: 60 - test_transmit(cfg, ipv4, extra_args) 63 + test_transmit(cfg, ipver, extra_args) 61 64 else: 62 - test_receive(cfg, ipv4, extra_args) 65 + test_receive(cfg, ipver, extra_args) 63 66 64 - if ipv4: 65 - f.__name__ = "ipv4_" + name 66 - else: 67 - f.__name__ = "ipv6_" + name 67 + f.__name__ = f"ipv{ipver}_" + name 68 68 return f 69 69 70 70 ··· 92 104 cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) 93 105 94 106 cases = [] 95 - for ipv4 in [True, False]: 96 - cases.append(test_builder("rx_tcp", cfg, ipv4, False, "-t")) 97 - cases.append(test_builder("rx_tcp_invalid", cfg, ipv4, False, "-t -E")) 107 + for ipver in ["4", "6"]: 108 + cases.append(test_builder("rx_tcp", cfg, ipver, False, "-t")) 109 + cases.append(test_builder("rx_tcp_invalid", cfg, ipver, False, "-t -E")) 98 110 99 - cases.append(test_builder("rx_udp", cfg, ipv4, False, "")) 100 - cases.append(test_builder("rx_udp_invalid", cfg, ipv4, False, "-E")) 111 + cases.append(test_builder("rx_udp", cfg, ipver, False, "")) 112 + cases.append(test_builder("rx_udp_invalid", cfg, ipver, False, "-E")) 101 113 102 - cases.append(test_builder("tx_udp_csum_offload", cfg, ipv4, True, "-U")) 103 - cases.append(test_builder("tx_udp_zero_checksum", cfg, ipv4, True, "-U -Z")) 114 + cases.append(test_builder("tx_udp_csum_offload", cfg, ipver, True, "-U")) 115 + cases.append(test_builder("tx_udp_zero_checksum", cfg, ipver, True, "-U -Z")) 104 116 105 117 ksft_run(cases=cases, args=(cfg, )) 106 118 ksft_exit()
+3 -3
tools/testing/selftests/drivers/net/hw/devmem.py
··· 21 21 22 22 @ksft_disruptive 23 23 def check_rx(cfg) -> None: 24 - cfg.require_v6() 24 + cfg.require_ipver("6") 25 25 require_devmem(cfg) 26 26 27 27 port = rand_port() 28 - listen_cmd = f"./ncdevmem -l -f {cfg.ifname} -s {cfg.v6} -p {port}" 28 + listen_cmd = f"./ncdevmem -l -f {cfg.ifname} -s {cfg.addr_v['6']} -p {port}" 29 29 30 30 with bkg(listen_cmd) as socat: 31 31 wait_port_listen(port) 32 - cmd(f"echo -e \"hello\\nworld\"| socat -u - TCP6:[{cfg.v6}]:{port}", host=cfg.remote, shell=True) 32 + cmd(f"echo -e \"hello\\nworld\"| socat -u - TCP6:[{cfg.addr_v['6']}]:{port}", host=cfg.remote, shell=True) 33 33 34 34 ksft_eq(socat.stdout.strip(), "hello\nworld") 35 35
+241
tools/testing/selftests/drivers/net/hw/tso.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + """Run the tools/testing/selftests/net/csum testsuite.""" 5 + 6 + import fcntl 7 + import socket 8 + import struct 9 + import termios 10 + import time 11 + 12 + from lib.py import ksft_pr, ksft_run, ksft_exit, KsftSkipEx, KsftXfailEx 13 + from lib.py import ksft_eq, ksft_ge, ksft_lt 14 + from lib.py import EthtoolFamily, NetdevFamily, NetDrvEpEnv 15 + from lib.py import bkg, cmd, defer, ethtool, ip, rand_port, wait_port_listen 16 + 17 + 18 + def sock_wait_drain(sock, max_wait=1000): 19 + """Wait for all pending write data on the socket to get ACKed.""" 20 + for _ in range(max_wait): 21 + one = b'\0' * 4 22 + outq = fcntl.ioctl(sock.fileno(), termios.TIOCOUTQ, one) 23 + outq = struct.unpack("I", outq)[0] 24 + if outq == 0: 25 + break 26 + time.sleep(0.01) 27 + ksft_eq(outq, 0) 28 + 29 + 30 + def tcp_sock_get_retrans(sock): 31 + """Get the number of retransmissions for the TCP socket.""" 32 + info = sock.getsockopt(socket.SOL_TCP, socket.TCP_INFO, 512) 33 + return struct.unpack("I", info[100:104])[0] 34 + 35 + 36 + def run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso): 37 + cfg.require_cmd("socat", remote=True) 38 + 39 + port = rand_port() 40 + listen_cmd = f"socat -{ipver} -t 2 -u TCP-LISTEN:{port},reuseport /dev/null,ignoreeof" 41 + 42 + with bkg(listen_cmd, host=cfg.remote) as nc: 43 + wait_port_listen(port, host=cfg.remote) 44 + 45 + if ipver == "4": 46 + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 47 + sock.connect((remote_v4, port)) 48 + else: 49 + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 50 + sock.connect((remote_v6, port)) 51 + 52 + # Small send to make sure the connection is working. 53 + sock.send("ping".encode()) 54 + sock_wait_drain(sock) 55 + 56 + # Send 4MB of data, record the LSO packet count. 57 + qstat_old = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 58 + buf = b"0" * 1024 * 1024 * 4 59 + sock.send(buf) 60 + sock_wait_drain(sock) 61 + qstat_new = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 62 + 63 + # No math behind the 10 here, but try to catch cases where 64 + # TCP falls back to non-LSO. 65 + ksft_lt(tcp_sock_get_retrans(sock), 10) 66 + sock.close() 67 + 68 + # Check that at least 90% of the data was sent as LSO packets. 69 + # System noise may cause false negatives. Also header overheads 70 + # will add up to 5% of extra packes... The check is best effort. 71 + total_lso_wire = len(buf) * 0.90 // cfg.dev["mtu"] 72 + total_lso_super = len(buf) * 0.90 // cfg.dev["tso_max_size"] 73 + if should_lso: 74 + if cfg.have_stat_super_count: 75 + ksft_ge(qstat_new['tx-hw-gso-packets'] - 76 + qstat_old['tx-hw-gso-packets'], 77 + total_lso_super, 78 + comment="Number of LSO super-packets with LSO enabled") 79 + if cfg.have_stat_wire_count: 80 + ksft_ge(qstat_new['tx-hw-gso-wire-packets'] - 81 + qstat_old['tx-hw-gso-wire-packets'], 82 + total_lso_wire, 83 + comment="Number of LSO wire-packets with LSO enabled") 84 + else: 85 + if cfg.have_stat_super_count: 86 + ksft_lt(qstat_new['tx-hw-gso-packets'] - 87 + qstat_old['tx-hw-gso-packets'], 88 + 15, comment="Number of LSO super-packets with LSO disabled") 89 + if cfg.have_stat_wire_count: 90 + ksft_lt(qstat_new['tx-hw-gso-wire-packets'] - 91 + qstat_old['tx-hw-gso-wire-packets'], 92 + 500, comment="Number of LSO wire-packets with LSO disabled") 93 + 94 + 95 + def build_tunnel(cfg, outer_ipver, tun_info): 96 + local_v4 = NetDrvEpEnv.nsim_v4_pfx + "1" 97 + local_v6 = NetDrvEpEnv.nsim_v6_pfx + "1" 98 + remote_v4 = NetDrvEpEnv.nsim_v4_pfx + "2" 99 + remote_v6 = NetDrvEpEnv.nsim_v6_pfx + "2" 100 + 101 + local_addr = cfg.addr_v[outer_ipver] 102 + remote_addr = cfg.remote_addr_v[outer_ipver] 103 + 104 + tun_type = tun_info[0] 105 + tun_arg = tun_info[2] 106 + ip(f"link add {tun_type}-ksft type {tun_type} {tun_arg} local {local_addr} remote {remote_addr} dev {cfg.ifname}") 107 + defer(ip, f"link del {tun_type}-ksft") 108 + ip(f"link set dev {tun_type}-ksft up") 109 + ip(f"addr add {local_v4}/24 dev {tun_type}-ksft") 110 + ip(f"addr add {local_v6}/64 dev {tun_type}-ksft") 111 + 112 + ip(f"link add {tun_type}-ksft type {tun_type} {tun_arg} local {remote_addr} remote {local_addr} dev {cfg.remote_ifname}", 113 + host=cfg.remote) 114 + defer(ip, f"link del {tun_type}-ksft", host=cfg.remote) 115 + ip(f"link set dev {tun_type}-ksft up", host=cfg.remote) 116 + ip(f"addr add {remote_v4}/24 dev {tun_type}-ksft", host=cfg.remote) 117 + ip(f"addr add {remote_v6}/64 dev {tun_type}-ksft", host=cfg.remote) 118 + 119 + return remote_v4, remote_v6 120 + 121 + 122 + def test_builder(name, cfg, outer_ipver, feature, tun=None, inner_ipver=None): 123 + """Construct specific tests from the common template.""" 124 + def f(cfg): 125 + cfg.require_ipver(outer_ipver) 126 + 127 + if not cfg.have_stat_super_count and \ 128 + not cfg.have_stat_wire_count: 129 + raise KsftSkipEx(f"Device does not support LSO queue stats") 130 + 131 + ipver = outer_ipver 132 + if tun: 133 + remote_v4, remote_v6 = build_tunnel(cfg, ipver, tun) 134 + ipver = inner_ipver 135 + else: 136 + remote_v4 = cfg.remote_addr_v["4"] 137 + remote_v6 = cfg.remote_addr_v["6"] 138 + 139 + tun_partial = tun and tun[1] 140 + # Tunnel which can silently fall back to gso-partial 141 + has_gso_partial = tun and 'tx-gso-partial' in cfg.features 142 + 143 + # For TSO4 via partial we need mangleid 144 + if ipver == "4" and feature in cfg.partial_features: 145 + ksft_pr("Testing with mangleid enabled") 146 + if 'tx-tcp-mangleid-segmentation' not in cfg.features: 147 + ethtool(f"-K {cfg.ifname} tx-tcp-mangleid-segmentation on") 148 + defer(ethtool, f"-K {cfg.ifname} tx-tcp-mangleid-segmentation off") 149 + 150 + # First test without the feature enabled. 151 + ethtool(f"-K {cfg.ifname} {feature} off") 152 + if has_gso_partial: 153 + ethtool(f"-K {cfg.ifname} tx-gso-partial off") 154 + run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=False) 155 + 156 + # Now test with the feature enabled. 157 + # For compatible tunnels only - just GSO partial, not specific feature. 158 + if has_gso_partial: 159 + ethtool(f"-K {cfg.ifname} tx-gso-partial on") 160 + run_one_stream(cfg, ipver, remote_v4, remote_v6, 161 + should_lso=tun_partial) 162 + 163 + # Full feature enabled. 164 + if feature in cfg.features: 165 + ethtool(f"-K {cfg.ifname} {feature} on") 166 + run_one_stream(cfg, ipver, remote_v4, remote_v6, should_lso=True) 167 + else: 168 + raise KsftXfailEx(f"Device does not support {feature}") 169 + 170 + f.__name__ = name + ((outer_ipver + "_") if tun else "") + "ipv" + inner_ipver 171 + return f 172 + 173 + 174 + def query_nic_features(cfg) -> None: 175 + """Query and cache the NIC features.""" 176 + cfg.have_stat_super_count = False 177 + cfg.have_stat_wire_count = False 178 + 179 + cfg.features = set() 180 + features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) 181 + for f in features["active"]["bits"]["bit"]: 182 + cfg.features.add(f["name"]) 183 + 184 + # Check which features are supported via GSO partial 185 + cfg.partial_features = set() 186 + if 'tx-gso-partial' in cfg.features: 187 + ethtool(f"-K {cfg.ifname} tx-gso-partial off") 188 + 189 + no_partial = set() 190 + features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) 191 + for f in features["active"]["bits"]["bit"]: 192 + no_partial.add(f["name"]) 193 + cfg.partial_features = cfg.features - no_partial 194 + ethtool(f"-K {cfg.ifname} tx-gso-partial on") 195 + 196 + stats = cfg.netnl.qstats_get({"ifindex": cfg.ifindex}, dump=True) 197 + if stats: 198 + if 'tx-hw-gso-packets' in stats[0]: 199 + ksft_pr("Detected qstat for LSO super-packets") 200 + cfg.have_stat_super_count = True 201 + if 'tx-hw-gso-wire-packets' in stats[0]: 202 + ksft_pr("Detected qstat for LSO wire-packets") 203 + cfg.have_stat_wire_count = True 204 + 205 + 206 + def main() -> None: 207 + with NetDrvEpEnv(__file__, nsim_test=False) as cfg: 208 + cfg.ethnl = EthtoolFamily() 209 + cfg.netnl = NetdevFamily() 210 + 211 + query_nic_features(cfg) 212 + 213 + test_info = ( 214 + # name, v4/v6 ethtool_feature tun:(type, partial, args) 215 + ("", "4", "tx-tcp-segmentation", None), 216 + ("", "6", "tx-tcp6-segmentation", None), 217 + ("vxlan", "", "tx-udp_tnl-segmentation", ("vxlan", True, "id 100 dstport 4789 noudpcsum")), 218 + ("vxlan_csum", "", "tx-udp_tnl-csum-segmentation", ("vxlan", False, "id 100 dstport 4789 udpcsum")), 219 + ("gre", "4", "tx-gre-segmentation", ("ipgre", False, "")), 220 + ("gre", "6", "tx-gre-segmentation", ("ip6gre", False, "")), 221 + ) 222 + 223 + cases = [] 224 + for outer_ipver in ["4", "6"]: 225 + for info in test_info: 226 + # Skip if test which only works for a specific IP version 227 + if info[1] and outer_ipver != info[1]: 228 + continue 229 + 230 + cases.append(test_builder(info[0], cfg, outer_ipver, info[2], 231 + tun=info[3], inner_ipver="4")) 232 + if info[3]: 233 + cases.append(test_builder(info[0], cfg, outer_ipver, info[2], 234 + tun=info[3], inner_ipver="6")) 235 + 236 + ksft_run(cases=cases, args=(cfg, )) 237 + ksft_exit() 238 + 239 + 240 + if __name__ == "__main__": 241 + main()
+36 -22
tools/testing/selftests/drivers/net/lib/py/env.py
··· 64 64 self._ns = None 65 65 66 66 if 'NETIF' in self.env: 67 - self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0] 67 + self.dev = ip("-d link show dev " + self.env['NETIF'], json=True)[0] 68 68 else: 69 69 self._ns = NetdevSimDev(**kwargs) 70 70 self.dev = self._ns.nsims[0].dev ··· 113 113 self._ns = None 114 114 self._ns_peer = None 115 115 116 + self.addr_v = { "4": None, "6": None } 117 + self.remote_addr_v = { "4": None, "6": None } 118 + 116 119 if "NETIF" in self.env: 117 120 if nsim_test is True: 118 121 raise KsftXfailEx("Test only works on netdevsim") 119 122 self._check_env() 120 123 121 - self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0] 124 + self.dev = ip("-d link show dev " + self.env['NETIF'], json=True)[0] 122 125 123 - self.v4 = self.env.get("LOCAL_V4") 124 - self.v6 = self.env.get("LOCAL_V6") 125 - self.remote_v4 = self.env.get("REMOTE_V4") 126 - self.remote_v6 = self.env.get("REMOTE_V6") 126 + self.addr_v["4"] = self.env.get("LOCAL_V4") 127 + self.addr_v["6"] = self.env.get("LOCAL_V6") 128 + self.remote_addr_v["4"] = self.env.get("REMOTE_V4") 129 + self.remote_addr_v["6"] = self.env.get("REMOTE_V6") 127 130 kind = self.env["REMOTE_TYPE"] 128 131 args = self.env["REMOTE_ARGS"] 129 132 else: ··· 137 134 138 135 self.dev = self._ns.nsims[0].dev 139 136 140 - self.v4 = self.nsim_v4_pfx + "1" 141 - self.v6 = self.nsim_v6_pfx + "1" 142 - self.remote_v4 = self.nsim_v4_pfx + "2" 143 - self.remote_v6 = self.nsim_v6_pfx + "2" 137 + self.addr_v["4"] = self.nsim_v4_pfx + "1" 138 + self.addr_v["6"] = self.nsim_v6_pfx + "1" 139 + self.remote_addr_v["4"] = self.nsim_v4_pfx + "2" 140 + self.remote_addr_v["6"] = self.nsim_v6_pfx + "2" 144 141 kind = "netns" 145 142 args = self._netns.name 146 143 147 144 self.remote = Remote(kind, args, src_path) 148 145 149 - self.addr = self.v6 if self.v6 else self.v4 150 - self.remote_addr = self.remote_v6 if self.remote_v6 else self.remote_v4 146 + self.addr_ipver = "6" if self.addr_v["6"] else "4" 147 + self.addr = self.addr_v[self.addr_ipver] 148 + self.remote_addr = self.remote_addr_v[self.addr_ipver] 151 149 152 - self.addr_ipver = "6" if self.v6 else "4" 153 150 # Bracketed addresses, some commands need IPv6 to be inside [] 154 - self.baddr = f"[{self.v6}]" if self.v6 else self.v4 155 - self.remote_baddr = f"[{self.remote_v6}]" if self.remote_v6 else self.remote_v4 151 + self.baddr = f"[{self.addr_v['6']}]" if self.addr_v["6"] else self.addr_v["4"] 152 + self.remote_baddr = f"[{self.remote_addr_v['6']}]" if self.remote_addr_v["6"] else self.remote_addr_v["4"] 156 153 157 154 self.ifname = self.dev['ifname'] 158 155 self.ifindex = self.dev['ifindex'] 156 + 157 + # resolve remote interface name 158 + self.remote_ifname = self.resolve_remote_ifc() 159 159 160 160 self._required_cmd = {} 161 161 ··· 206 200 raise Exception("Invalid environment, missing configuration:", missing, 207 201 "Please see tools/testing/selftests/drivers/net/README.rst") 208 202 203 + def resolve_remote_ifc(self): 204 + v4 = v6 = None 205 + if self.remote_addr_v["4"]: 206 + v4 = ip("addr show to " + self.remote_addr_v["4"], json=True, host=self.remote) 207 + if self.remote_addr_v["6"]: 208 + v6 = ip("addr show to " + self.remote_addr_v["6"], json=True, host=self.remote) 209 + if v4 and v6 and v4[0]["ifname"] != v6[0]["ifname"]: 210 + raise Exception("Can't resolve remote interface name, v4 and v6 don't match") 211 + if (v4 and len(v4) > 1) or (v6 and len(v6) > 1): 212 + raise Exception("Can't resolve remote interface name, multiple interfaces match") 213 + return v6[0]["ifname"] if v6 else v4[0]["ifname"] 214 + 209 215 def __enter__(self): 210 216 return self 211 217 ··· 241 223 del self.remote 242 224 self.remote = None 243 225 244 - def require_v4(self): 245 - if not self.v4 or not self.remote_v4: 246 - raise KsftSkipEx("Test requires IPv4 connectivity") 247 - 248 - def require_v6(self): 249 - if not self.v6 or not self.remote_v6: 250 - raise KsftSkipEx("Test requires IPv6 connectivity") 226 + def require_ipver(self, ipver): 227 + if not self.addr_v[ipver] or not self.remote_addr_v[ipver]: 228 + raise KsftSkipEx(f"Test requires IPv{ipver} connectivity") 251 229 252 230 def _require_cmd(self, comm, key, host=None): 253 231 cached = self._required_cmd.get(comm, {})
+6 -6
tools/testing/selftests/drivers/net/ping.py
··· 8 8 9 9 10 10 def test_v4(cfg) -> None: 11 - cfg.require_v4() 11 + cfg.require_ipver("4") 12 12 13 - cmd(f"ping -c 1 -W0.5 {cfg.remote_v4}") 14 - cmd(f"ping -c 1 -W0.5 {cfg.v4}", host=cfg.remote) 13 + cmd("ping -c 1 -W0.5 " + cfg.remote_addr_v["4"]) 14 + cmd("ping -c 1 -W0.5 " + cfg.addr_v["4"], host=cfg.remote) 15 15 16 16 17 17 def test_v6(cfg) -> None: 18 - cfg.require_v6() 18 + cfg.require_ipver("6") 19 19 20 - cmd(f"ping -c 1 -W0.5 {cfg.remote_v6}") 21 - cmd(f"ping -c 1 -W0.5 {cfg.v6}", host=cfg.remote) 20 + cmd("ping -c 1 -W0.5 " + cfg.remote_addr_v["6"]) 21 + cmd("ping -c 1 -W0.5 " + cfg.addr_v["6"], host=cfg.remote) 22 22 23 23 24 24 def test_tcp(cfg) -> None: