vcl: fix cl sendto on unbound socket 71/42471/9
authorFlorin Coras <[email protected]>
Wed, 12 Mar 2025 08:18:32 +0000 (04:18 -0400)
committerDave Wallace <[email protected]>
Fri, 14 Mar 2025 01:59:30 +0000 (01:59 +0000)
Type: fix

Change-Id: Ia1e61a7ed633ad9708bd02fbaf4a39fe1a0ca1f3
Signed-off-by: Florin Coras <[email protected]>
src/plugins/hs_apps/CMakeLists.txt
src/plugins/hs_apps/vcl/vcl_test_cl_udp.c [new file with mode: 0644]
src/vcl/vppcom.c
test/asf/test_vcl.py

index eae1009..7cccf14 100644 (file)
@@ -74,4 +74,9 @@ if(VPP_BUILD_VCL_TESTS)
       NO_INSTALL
     )
   endforeach()
+
+  add_vpp_executable(vcl_test_cl_udp SOURCES "vcl/vcl_test_cl_udp.c"
+    LINK_LIBRARIES vppcom pthread ${EPOLL_LIB}
+    NO_INSTALL
+  )
 endif(VPP_BUILD_VCL_TESTS)
diff --git a/src/plugins/hs_apps/vcl/vcl_test_cl_udp.c b/src/plugins/hs_apps/vcl/vcl_test_cl_udp.c
new file mode 100644 (file)
index 0000000..066635e
--- /dev/null
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <vcl/vppcom.h>
+#include <hs_apps/vcl/vcl_test.h>
+
+typedef enum vt_clu_type_
+{
+  VT_CLU_TYPE_NONE = 0,
+  VT_CLU_TYPE_SERVER,
+  VT_CLU_TYPE_CLIENT,
+} vt_clu_type_t;
+
+typedef struct vtclu_main_
+{
+  vt_clu_type_t app_type;
+  vppcom_endpt_t endpt;
+  union
+  {
+    struct sockaddr_storage srvr_addr;
+    struct sockaddr_storage clnt_addr;
+  };
+  uint16_t port;
+} vt_clu_main_t;
+
+static vt_clu_main_t vt_clu_main;
+
+static void
+vt_clu_parse_args (vt_clu_main_t *vclum, int argc, char **argv)
+{
+  int c;
+
+  memset (vclum, 0, sizeof (*vclum));
+  vclum->port = VCL_TEST_SERVER_PORT;
+
+  opterr = 0;
+  while ((c = getopt (argc, argv, "s:c:")) != -1)
+    switch (c)
+      {
+      case 's':
+       vclum->app_type = VT_CLU_TYPE_SERVER;
+       if (inet_pton (
+             AF_INET, optarg,
+             &((struct sockaddr_in *) &vclum->srvr_addr)->sin_addr) != 1)
+         vtwrn ("couldn't parse ipv4 addr %s", optarg);
+       break;
+      case 'c':
+       vclum->app_type = VT_CLU_TYPE_CLIENT;
+       if (inet_pton (
+             AF_INET, optarg,
+             &((struct sockaddr_in *) &vclum->clnt_addr)->sin_addr) != 1)
+         break;
+      }
+
+  if (vclum->app_type == VT_CLU_TYPE_NONE)
+    {
+      vtwrn ("client or server must be configured");
+      exit (1);
+    }
+
+  vclum->endpt.is_ip4 = 1;
+  vclum->endpt.ip =
+    (uint8_t *) &((struct sockaddr_in *) &vclum->srvr_addr)->sin_addr;
+  vclum->endpt.port = htons (vclum->endpt.port);
+}
+
+int
+main (int argc, char **argv)
+{
+  vt_clu_main_t *vclum = &vt_clu_main;
+  int rv, vcl_sh;
+  const int buflen = 64;
+  char buf[buflen];
+
+  struct sockaddr_in _addr;
+  vppcom_endpt_t rmt_ep = { .ip = (void *) &_addr };
+
+  vt_clu_parse_args (vclum, argc, argv);
+
+  rv = vppcom_app_create ("vcl_test_cl_udp");
+  if (rv)
+    vtfail ("vppcom_app_create()", rv);
+
+  vcl_sh = vppcom_session_create (VPPCOM_PROTO_UDP, 0 /* is_nonblocking */);
+  if (vcl_sh < 0)
+    {
+      vterr ("vppcom_session_create()", vcl_sh);
+      return vcl_sh;
+    }
+
+  if (vclum->app_type == VT_CLU_TYPE_SERVER)
+    {
+      /* Listen is implicit */
+      rv = vppcom_session_bind (vcl_sh, &vclum->endpt);
+      if (rv < 0)
+       {
+         vterr ("vppcom_session_bind()", rv);
+         return rv;
+       }
+
+      rv = vppcom_session_recvfrom (vcl_sh, buf, buflen, 0, &rmt_ep);
+      if (rv < 0)
+       {
+         vterr ("vppcom_session_recvfrom()", rv);
+         return rv;
+       }
+      buf[rv] = 0;
+      vtinf ("Received message from client: %s", buf);
+
+      char *msg = "hello cl udp client";
+      int msg_len = strnlen (msg, buflen);
+      memcpy (buf, msg, msg_len);
+      /* send 2 times to be sure */
+      for (int i = 0; i < 2; i++)
+       {
+         rv = vppcom_session_sendto (vcl_sh, buf, msg_len, 0, &rmt_ep);
+         if (rv < 0)
+           {
+             vterr ("vppcom_session_sendto()", rv);
+             return rv;
+           }
+         usleep (500);
+       }
+    }
+  else if (vclum->app_type == VT_CLU_TYPE_CLIENT)
+    {
+      char *msg = "hello cl udp server";
+      int msg_len = strnlen (msg, buflen);
+      memcpy (buf, msg, msg_len);
+
+      /* send 3 times to be sure */
+      for (int i = 0; i < 3; i++)
+       {
+         rv = vppcom_session_sendto (vcl_sh, buf, msg_len, 0, &vclum->endpt);
+         if (rv < 0)
+           {
+             vterr ("vppcom_session_sendto()", rv);
+             return rv;
+           }
+         usleep (500);
+       }
+
+      rv = vppcom_session_recvfrom (vcl_sh, buf, buflen, 0, &rmt_ep);
+      if (rv < 0)
+       {
+         vterr ("vppcom_session_recvfrom()", rv);
+         return rv;
+       }
+      buf[rv] = 0;
+      vtinf ("Received message from server: %s", buf);
+    }
+}
\ No newline at end of file
index b4f985e..1e9c915 100644 (file)
@@ -4557,17 +4557,31 @@ vppcom_session_sendto (uint32_t session_handle, void *buffer,
       if (ep->app_tlvs)
        vcl_handle_ep_app_tlvs (s, ep);
 
-      /* Session not connected/bound in vpp. Create it by 'connecting' it */
+      /* Session not connected/bound in vpp. Create it by binding it */
       if (PREDICT_FALSE (s->session_state == VCL_STATE_CLOSED))
        {
          u32 session_index = s->session_index;
          f64 timeout = vcm->cfg.session_timeout;
          int rv;
 
-         vcl_send_session_connect (wrk, s);
-         rv = vppcom_wait_for_session_state_change (session_index,
-                                                    VCL_STATE_READY,
-                                                    timeout);
+         /* VPP assumes sockets are bound, not ideal, but for now
+          * connect socket, grab lcl ip:port pair and use it to bind */
+         if (s->transport.rmt_port == 0 ||
+             ip46_address_is_zero (&s->transport.lcl_ip))
+           {
+             vcl_send_session_connect (wrk, s);
+             rv = vppcom_wait_for_session_state_change (
+               session_index, VCL_STATE_READY, timeout);
+             if (rv < 0)
+               return rv;
+             vcl_send_session_disconnect (wrk, s);
+             rv = vppcom_wait_for_session_state_change (
+               session_index, VCL_STATE_DETACHED, timeout);
+             s->session_state = VCL_STATE_CLOSED;
+           }
+         vcl_send_session_listen (wrk, s);
+         rv = vppcom_wait_for_session_state_change (
+           session_index, VCL_STATE_LISTEN, timeout);
          if (rv < 0)
            return rv;
          s = vcl_session_get (wrk, session_index);
index 143b46c..ae837fa 100644 (file)
@@ -561,6 +561,48 @@ class VCLThruHostStackEcho(VCLTestCase):
         self.logger.debug(self.vapi.cli("show app mq"))
 
 
+    "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
+)
+class VCLThruHostStackCLUDPEcho(VCLTestCase):
+    """VCL Thru Host Stack CL UDP Echo"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(VCLThruHostStackCLUDPEcho, cls).setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(VCLThruHostStackCLUDPEcho, cls).tearDownClass()
+
+    def setUp(self):
+        super(VCLThruHostStackCLUDPEcho, self).setUp()
+
+        self.thru_host_stack_setup()
+        self.pre_test_sleep = 2
+        self.timeout = 5
+
+    def tearDown(self):
+        self.thru_host_stack_tear_down()
+        super(VCLThruHostStackCLUDPEcho, self).tearDown()
+
+    def test_vcl_thru_host_stack_cl_udp_echo(self):
+        """run VCL IPv4 thru host stack CL UDP echo test"""
+        server_args = ["-s", self.loop0.local_ip4]
+        client_args = ["-c", self.loop0.local_ip4]
+        self.thru_host_stack_test(
+            "vcl_test_cl_udp",
+            server_args,
+            "vcl_test_cl_udp",
+            client_args,
+        )
+
+    def show_commands_at_teardown(self):
+        self.logger.debug(self.vapi.cli("show app server"))
+        self.logger.debug(self.vapi.cli("show session verbose"))
+        self.logger.debug(self.vapi.cli("show app mq"))
+
+
 @unittest.skipIf(
     "hs_apps" in config.excluded_plugins, "Exclude tests requiring hs_apps plugin"
 )