Subject: Re: [PATCH net-next v3 8/8] selftests: drv-net: add a TCP ping test case (and useful helpers)

From: Jakub Kicinski <[email-masked]>
Date: Thu, 18 Apr 2024 12:48:12 -0700

On Thu, 18 Apr 2024 10:44:39 -0400 Willem de Bruijn wrote:
> > +def test_tcp(cfg) -> None:
> > +    port = random.randrange(1024 + (1 << 15))
> > +    with bkg(f"nc -l {cfg.addr} {port}") as nc:
> > +        wait_port_listen(port)
> > +
> > +        cmd(f"echo ping | nc {cfg.addr} {port}",
> > +            shell=True, host=cfg.remote)
> > +    ksft_eq(nc.stdout.strip(), "ping")
> > +
> > +    port = random.randrange(1024 + (1 << 15))
> > +    with bkg(f"nc -l {cfg.remote_addr} {port}", host=cfg.remote) as nc:
> > +        wait_port_listen(port, host=cfg.remote)
> > +
> > +        cmd(f"echo ping | nc {cfg.remote_addr} {port}", shell=True)
> > +    ksft_eq(nc.stdout.strip(), "ping")
> > +  
> 
> There are different netcat implementations floating around.
> 
> I notice that I have to pass -N on the client to terminate the
> connection after EOF. Else both peers keep the connection open,
> waiting for input. And explicitly pass -6 if passing an IPv6
> address. I think this is the one that ships with Debian..

Right, 100% laziness on my part. Mostly because socat requires
bracketed IPv6. But once I tried it I also run into the premature
termination problem, so ended up with this diff on top:

diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 579c5b34e6fd..2f62270d59fa 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -110,6 +110,10 @@ from .remote import Remote
         self.addr = self.v6 if self.v6 else self.v4
         self.remote_addr = self.remote_v6 if self.remote_v6 else self.remote_v4
 
+        # Bracketed addresses, some commands need IPv6 to be inside []
+        self.baddr = f"[{self.v6}]" if self.v6 else self.v4
+        self.remote_baddr = f"[{self.remote_v6}]" if self.remote_v6 else self.remote_v4
+
         self.ifname = self.dev['ifname']
         self.ifindex = self.dev['ifindex']
 
diff --git a/tools/testing/selftests/drivers/net/ping.py b/tools/testing/selftests/drivers/net/ping.py
index 8532e3be72ba..985b06ce2e81 100755
--- a/tools/testing/selftests/drivers/net/ping.py
+++ b/tools/testing/selftests/drivers/net/ping.py
@@ -27,18 +27,20 @@ from lib.py import bkg, cmd, wait_port_listen
 
 def test_tcp(cfg) -> None:
     port = random.randrange(1024 + (1 << 15))
-    with bkg(f"nc -l {cfg.addr} {port}") as nc:
+
+    with bkg(f"socat -t 2 -u TCP-LISTEN:{port} STDOUT", exit_wait=True) as nc:
         wait_port_listen(port)
 
-        cmd(f"echo ping | nc {cfg.addr} {port}",
+        cmd(f"echo ping | socat -t 2 -u STDIN TCP:{cfg.baddr}:{port}",
             shell=True, host=cfg.remote)
     ksft_eq(nc.stdout.strip(), "ping")
 
     port = random.randrange(1024 + (1 << 15))
-    with bkg(f"nc -l {cfg.remote_addr} {port}", host=cfg.remote) as nc:
+    with bkg(f"socat -t 2 -u TCP-LISTEN:{port} STDOUT", host=cfg.remote,
+             exit_wait=True) as nc:
         wait_port_listen(port, host=cfg.remote)
 
-        cmd(f"echo ping | nc {cfg.remote_addr} {port}", shell=True)
+        cmd(f"echo ping | socat -t 2 -u STDIN TCP:{cfg.remote_baddr}:{port}", shell=True)
     ksft_eq(nc.stdout.strip(), "ping")

diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 6bacdc99d21b..85a6a9bb35fd 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -42,15 +42,17 @@ import time

 class bkg(cmd):
-    def __init__(self, comm, shell=True, fail=True, ns=None, host=None):
+    def __init__(self, comm, shell=True, fail=True, ns=None, host=None,
+                 exit_wait=False):
         super().__init__(comm, background=True,
                          shell=shell, fail=fail, ns=ns, host=host)
+        self.terminate = not exit_wait
 
     def __enter__(self):
         return self
 
     def __exit__(self, ex_type, ex_value, ex_tb):
-        return self.process()
+        return self.process(terminate=self.terminate)

 def ip(args, json=None, ns=None, host=None):