vcl: add QUIC support 50/19650/17
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>
Thu, 16 May 2019 12:38:44 +0000 (14:38 +0200)
committerFlorin Coras <florin.coras@gmail.com>
Thu, 18 Jul 2019 18:19:05 +0000 (18:19 +0000)
Type: feature

* Adds the concept of a "connectable listener" : a session that
 can be both connected and accepted on.
* vppcom_session_is_connectable_listener (fd) that tells if the fd
is a connectable listener
* vppcom_session_listener (fd) that gives you the listener's fd
that accepted the session (if any)
* vppcom_session_n_accepted (fd) that gives the number
of sessions a listener accepted.

Change-Id: Id89d67d8339fb15a7cf7e00a9c5448175eca04fc
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
src/plugins/hs_apps/vcl/vcl_test.h
src/plugins/hs_apps/vcl/vcl_test_client.c
src/plugins/hs_apps/vcl/vcl_test_server.c
src/vcl/vcl_private.h
src/vcl/vppcom.c
src/vcl/vppcom.h

index ab05f7a..7586e29 100644 (file)
@@ -86,10 +86,11 @@ typedef struct __attribute__ ((packed))
   uint32_t test;
   uint32_t ctrl_handle;
   uint32_t num_test_sessions;
+  uint32_t num_test_sessions_perq;
+  uint32_t num_test_qsessions;
   uint32_t verbose;
   uint32_t address_ip6;
   uint32_t transport_udp;
-  uint32_t transport_tls;
   uint64_t rxbuf_size;
   uint64_t txbuf_size;
   uint64_t num_writes;
@@ -119,6 +120,7 @@ typedef struct
   char *rxbuf;
   vcl_test_cfg_t cfg;
   vcl_test_stats_t stats;
+  int session_index;
 } vcl_test_session_t;
 
 
@@ -201,6 +203,7 @@ vcl_test_cfg_init (vcl_test_cfg_t * cfg)
   cfg->test = VCL_TEST_TYPE_NONE;
   cfg->ctrl_handle = ~0;
   cfg->num_test_sessions = 1;
+  cfg->num_test_sessions_perq = 1;
   cfg->verbose = 0;
   cfg->rxbuf_size = VCL_TEST_CFG_RXBUF_SIZE_DEF;
   cfg->num_writes = VCL_TEST_CFG_NUM_WRITES_DEF;
@@ -491,28 +494,16 @@ vcl_test_write (int fd, uint8_t * buf, uint32_t nbytes,
       if (rv < 0)
        {
          errno = -rv;
-         rv = -1;
-       }
-      if (rv < 0)
-       {
-         if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
-           {
-             if (stats)
-               stats->tx_eagain++;
-             break;
-           }
-         else
-           break;
+         if ((errno == EAGAIN || errno == EWOULDBLOCK) && stats)
+           stats->tx_eagain++;
+         break;
        }
       tx_bytes += rv;
 
-      if (tx_bytes != nbytes)
-       {
-         nbytes_left = nbytes_left - rv;
-         buf += rv;
-         if (stats)
-           stats->tx_incomp++;
-       }
+      nbytes_left = nbytes_left - rv;
+      buf += rv;
+      if (stats)
+       stats->tx_incomp++;
 
     }
   while (tx_bytes != nbytes);
index 1ead146..b9bdd6e 100644 (file)
@@ -28,7 +28,9 @@
 typedef struct
 {
   vcl_test_session_t *sessions;
+  vcl_test_session_t *qsessions;
   uint32_t n_sessions;
+  uint32_t n_qsessions;
   uint32_t wrk_index;
   fd_set wr_fdset;
   fd_set rd_fdset;
@@ -42,11 +44,12 @@ typedef struct
   vcl_test_client_worker_t *workers;
   vppcom_endpt_t server_endpt;
   uint32_t cfg_seq_num;
+  vcl_test_session_t quic_session;
   vcl_test_session_t ctrl_session;
   vcl_test_session_t *sessions;
   uint8_t dump_cfg;
   vcl_test_t post_test;
-  uint32_t proto;
+  uint8_t proto;
   uint32_t n_workers;
   volatile int active_workers;
   struct sockaddr_storage server_addr;
@@ -115,6 +118,105 @@ vtc_cfg_sync (vcl_test_session_t * ts)
   return 0;
 }
 
+static int
+vtc_quic_connect_test_sessions (vcl_test_client_worker_t * wrk)
+{
+  vcl_test_client_main_t *vcm = &vcl_client_main;
+  vcl_test_session_t *ts, *tq;
+  uint32_t i;
+  int rv;
+
+  if (wrk->cfg.num_test_sessions < 1 || wrk->cfg.num_test_sessions_perq < 1)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (wrk->n_sessions >= wrk->cfg.num_test_sessions)
+    goto done;
+
+  /* Connect Qsessions */
+
+  if (wrk->n_qsessions)
+    wrk->qsessions =
+      realloc (wrk->qsessions,
+              wrk->cfg.num_test_qsessions * sizeof (vcl_test_session_t));
+  else
+    wrk->qsessions =
+      calloc (wrk->cfg.num_test_qsessions, sizeof (vcl_test_session_t));
+
+  if (!wrk->qsessions)
+    {
+      vterr ("failed to alloc Qsessions", -errno);
+      return errno;
+    }
+
+
+  for (i = 0; i < wrk->cfg.num_test_qsessions; i++)
+    {
+      tq = &wrk->qsessions[i];
+      tq->fd = vppcom_session_create (vcm->proto, 1 /* is_nonblocking */ );
+      tq->session_index = i;
+      if (tq->fd < 0)
+       {
+         vterr ("vppcom_session_create()", tq->fd);
+         return tq->fd;
+       }
+
+      rv = vppcom_session_connect (tq->fd, &vcm->server_endpt);
+      if (rv < 0)
+       {
+         vterr ("vppcom_session_connect()", rv);
+         return rv;
+       }
+      vtinf ("Test Qsession %d (fd %d) connected.", i, tq->fd);
+    }
+  wrk->n_qsessions = wrk->cfg.num_test_qsessions;
+
+  /* Connect Stream sessions */
+
+  if (wrk->n_sessions)
+    wrk->sessions =
+      realloc (wrk->sessions,
+              wrk->cfg.num_test_sessions * sizeof (vcl_test_session_t));
+  else
+    wrk->sessions =
+      calloc (wrk->cfg.num_test_sessions, sizeof (vcl_test_session_t));
+
+  if (!wrk->sessions)
+    {
+      vterr ("failed to alloc sessions", -errno);
+      return errno;
+    }
+
+  for (i = 0; i < wrk->cfg.num_test_sessions; i++)
+    {
+      tq = &wrk->qsessions[i / wrk->cfg.num_test_sessions_perq];
+      ts = &wrk->sessions[i];
+      ts->fd = vppcom_session_create (vcm->proto, 1 /* is_nonblocking */ );
+      ts->session_index = i;
+      if (ts->fd < 0)
+       {
+         vterr ("vppcom_session_create()", ts->fd);
+         return ts->fd;
+       }
+
+      rv = vppcom_session_stream_connect (ts->fd, tq->fd);
+      if (rv < 0)
+       {
+         vterr ("vppcom_session_stream_connect()", rv);
+         return rv;
+       }
+
+      vtinf ("Test session %d (fd %d) connected.", i, ts->fd);
+    }
+  wrk->n_sessions = wrk->cfg.num_test_sessions;
+
+done:
+  vtinf ("All test sessions (%d) connected!", wrk->cfg.num_test_sessions);
+  return 0;
+}
+
 static int
 vtc_connect_test_sessions (vcl_test_client_worker_t * wrk)
 {
@@ -123,6 +225,9 @@ vtc_connect_test_sessions (vcl_test_client_worker_t * wrk)
   uint32_t n_test_sessions;
   int i, rv;
 
+  if (vcm->proto == VPPCOM_PROTO_QUIC)
+    return vtc_quic_connect_test_sessions (wrk);
+
   n_test_sessions = wrk->cfg.num_test_sessions;
   if (n_test_sessions < 1)
     {
@@ -314,6 +419,7 @@ vtc_worker_sessions_exit (vcl_test_client_worker_t * wrk)
       (void) vcl_test_write (ts->fd, (uint8_t *) & ts->cfg,
                             sizeof (ts->cfg), &ts->stats, verbose);
     }
+
   wrk->n_sessions = 0;
 }
 
@@ -387,7 +493,6 @@ vtc_worker_loop (void *arg)
                  goto exit;
                }
            }
-
          if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes)
              || (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
            {
@@ -710,6 +815,7 @@ print_usage_and_exit (void)
           "  -c               Print test config before test.\n"
           "  -w <dir>         Write test results to <dir>.\n"
           "  -X               Exit after running test.\n"
+          "  -p <proto>       Use <proto> transport layer\n"
           "  -D               Use UDP transport layer\n"
           "  -L               Use TLS transport layer\n"
           "  -E               Run Echo test.\n"
@@ -718,7 +824,10 @@ print_usage_and_exit (void)
           "  -T <txbuf-size>  Test Cfg: tx buffer size.\n"
           "  -U               Run Uni-directional test.\n"
           "  -B               Run Bi-directional test.\n"
-          "  -V               Verbose mode.\n");
+          "  -V               Verbose mode.\n"
+          "  -I <N>           Use N sessions.\n"
+          "  -s <N>           Use N sessions.\n"
+          "  -q <n>           QUIC : use N Ssessions on top of n Qsessions\n");
   exit (1);
 }
 
@@ -729,13 +838,14 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
   int c, v;
 
   opterr = 0;
-  while ((c = getopt (argc, argv, "chn:w:XE:I:N:R:T:UBV6DL")) != -1)
+  while ((c = getopt (argc, argv, "chnp:w:XE:I:N:R:T:UBV6DLs:q:")) != -1)
     switch (c)
       {
       case 'c':
        vcm->dump_cfg = 1;
        break;
 
+      case 'I':                /* deprecated */
       case 's':
        if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions) != 1)
          if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sessions) != 1)
@@ -744,11 +854,30 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
              print_usage_and_exit ();
            }
        if (!ctrl->cfg.num_test_sessions ||
-           (ctrl->cfg.num_test_sessions > FD_SETSIZE))
+           (ctrl->cfg.num_test_sessions > VCL_TEST_CFG_MAX_TEST_SESS))
          {
            vtwrn ("Invalid number of sessions (%d) specified for option -%c!"
                   "\n       Valid range is 1 - %d",
-                  ctrl->cfg.num_test_sessions, c, FD_SETSIZE);
+                  ctrl->cfg.num_test_sessions, c,
+                  VCL_TEST_CFG_MAX_TEST_SESS);
+           print_usage_and_exit ();
+         }
+       break;
+
+      case 'q':
+       if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions_perq) != 1)
+         if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sessions_perq) != 1)
+           {
+             vtwrn ("Invalid value for option -%c!", c);
+             print_usage_and_exit ();
+           }
+       if (!ctrl->cfg.num_test_sessions_perq ||
+           (ctrl->cfg.num_test_sessions_perq > VCL_TEST_CFG_MAX_TEST_SESS))
+         {
+           vtwrn ("Invalid number of Stream sessions (%d) per Qsession"
+                  "for option -%c!\nValid range is 1 - %d",
+                  ctrl->cfg.num_test_sessions_perq, c,
+                  VCL_TEST_CFG_MAX_TEST_SESS);
            print_usage_and_exit ();
          }
        break;
@@ -778,21 +907,6 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
        ctrl->cfg.test = VCL_TEST_TYPE_ECHO;
        break;
 
-      case 'I':
-       if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions) != 1)
-         if (sscanf (optarg, "%d", &ctrl->cfg.num_test_sessions) != 1)
-           {
-             vtwrn ("Invalid value for option -%c!", c);
-             print_usage_and_exit ();
-           }
-       if (ctrl->cfg.num_test_sessions > VCL_TEST_CFG_MAX_TEST_SESS)
-         {
-           vtwrn ("value greater than max number test sessions (%d)!",
-                  VCL_TEST_CFG_MAX_TEST_SESS);
-           print_usage_and_exit ();
-         }
-       break;
-
       case 'N':
        if (sscanf (optarg, "0x%lx", &ctrl->cfg.num_writes) != 1)
          if (sscanf (optarg, "%ld", &ctrl->cfg.num_writes) != 1)
@@ -866,23 +980,30 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
        ctrl->cfg.address_ip6 = 1;
        break;
 
-      case 'D':
-       ctrl->cfg.transport_udp = 1;
+      case 'p':
+       if (vppcom_unformat_proto (&vcm->proto, optarg))
+         vtwrn ("Invalid vppcom protocol %s, defaulting to TCP", optarg);
        break;
 
-      case 'L':
-       ctrl->cfg.transport_tls = 1;
+      case 'D':                /* deprecated */
+       vcm->proto = VPPCOM_PROTO_UDP;
+       break;
+
+      case 'L':                /* deprecated */
+       vcm->proto = VPPCOM_PROTO_TLS;
        break;
 
       case '?':
        switch (optopt)
          {
          case 'E':
-         case 'I':
+         case 'I':             /* deprecated */
          case 'N':
          case 'R':
          case 'T':
          case 'w':
+         case 'p':
+         case 'q':
            vtwrn ("Option -%c requires an argument.", optopt);
            break;
 
@@ -904,18 +1025,9 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
       print_usage_and_exit ();
     }
 
-  if (ctrl->cfg.transport_udp)
-    {
-      vcm->proto = VPPCOM_PROTO_UDP;
-    }
-  else if (ctrl->cfg.transport_tls)
-    {
-      vcm->proto = VPPCOM_PROTO_TLS;
-    }
-  else
-    {
-      vcm->proto = VPPCOM_PROTO_TCP;
-    }
+  ctrl->cfg.num_test_qsessions = vcm->proto != VPPCOM_PROTO_QUIC ? 0 :
+    (ctrl->cfg.num_test_sessions + ctrl->cfg.num_test_sessions_perq - 1) /
+    ctrl->cfg.num_test_sessions_perq;
 
   memset (&vcm->server_addr, 0, sizeof (vcm->server_addr));
   if (ctrl->cfg.address_ip6)
@@ -985,6 +1097,7 @@ main (int argc, char **argv)
 {
   vcl_test_client_main_t *vcm = &vcl_client_main;
   vcl_test_session_t *ctrl = &vcm->ctrl_session;
+  vcl_test_session_t *quic_session = &vcm->quic_session;
   int rv;
 
   vcm->n_workers = 1;
@@ -1001,7 +1114,7 @@ main (int argc, char **argv)
   if (ctrl->fd < 0)
     vtfail ("vppcom_session_create()", ctrl->fd);
 
-  if (vcm->proto == VPPCOM_PROTO_TLS)
+  if (vcm->proto == VPPCOM_PROTO_TLS || vcm->proto == VPPCOM_PROTO_QUIC)
     {
       vtinf ("Adding tls certs ...");
       vppcom_session_tls_add_cert (ctrl->fd, vcl_test_crt_rsa,
@@ -1011,7 +1124,20 @@ main (int argc, char **argv)
     }
 
   vtinf ("Connecting to server...");
-  rv = vppcom_session_connect (ctrl->fd, &vcm->server_endpt);
+  if (vcm->proto == VPPCOM_PROTO_QUIC)
+    {
+      quic_session->fd = vppcom_session_create (vcm->proto,
+                                               0 /* is_nonblocking */ );
+      if (quic_session->fd < 0)
+       vtfail ("vppcom_session_create()", quic_session->fd);
+      rv = vppcom_session_connect (quic_session->fd, &vcm->server_endpt);
+      if (rv)
+       vtfail ("vppcom_session_connect()", rv);
+      vtinf ("Connecting to stream...");
+      rv = vppcom_session_stream_connect (ctrl->fd, quic_session->fd);
+    }
+  else
+    rv = vppcom_session_connect (ctrl->fd, &vcm->server_endpt);
   if (rv)
     vtfail ("vppcom_session_connect()", rv);
   vtinf ("Control session (fd %d) connected.", ctrl->fd);
@@ -1082,7 +1208,8 @@ main (int argc, char **argv)
     }
 
   vtc_ctrl_session_exit ();
-  vppcom_session_close (ctrl->fd);
+  if (quic_session)
+    vppcom_session_close (quic_session->fd);
   vppcom_app_destroy ();
   free (vcm->workers);
   return 0;
index 62292ad..be225fa 100644 (file)
@@ -277,7 +277,7 @@ vts_server_echo (vcl_test_server_conn_t * conn, int rx_bytes)
 }
 
 static void
-vts_new_client (vcl_test_server_worker_t * wrk)
+vts_new_client (vcl_test_server_worker_t * wrk, int listen_fd)
 {
   vcl_test_server_conn_t *conn;
   struct epoll_event ev;
@@ -290,7 +290,7 @@ vts_new_client (vcl_test_server_worker_t * wrk)
       return;
     }
 
-  client_fd = vppcom_session_accept (wrk->listen_fd, &conn->endpt, 0);
+  client_fd = vppcom_session_accept (listen_fd, &conn->endpt, 0);
   if (client_fd < 0)
     {
       vterr ("vppcom_session_accept()", client_fd);
@@ -298,7 +298,8 @@ vts_new_client (vcl_test_server_worker_t * wrk)
     }
   conn->fd = client_fd;
 
-  vtinf ("Got a connection -- fd = %d (0x%08x)!", client_fd, client_fd);
+  vtinf ("Got a connection -- fd = %d (0x%08x) on listener fd = %d (0x%08x)",
+        client_fd, client_fd, listen_fd, listen_fd);
 
   ev.events = EPOLLIN;
   ev.data.u64 = conn - wrk->conn_pool;
@@ -320,6 +321,7 @@ print_usage_and_exit (void)
           "  -h               Print this message and exit.\n"
           "  -6               Use IPv6\n"
           "  -w <num>         Number of workers\n"
+          "  -p <PROTO>       Use <PROTO> transport layer\n"
           "  -D               Use UDP transport layer\n"
           "  -L               Use TLS transport layer\n");
   exit (1);
@@ -371,13 +373,18 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
   vsm->cfg.proto = VPPCOM_PROTO_TCP;
 
   opterr = 0;
-  while ((c = getopt (argc, argv, "6DLsw:")) != -1)
+  while ((c = getopt (argc, argv, "6DLsw:p:")) != -1)
     switch (c)
       {
       case '6':
        vsm->cfg.address_ip6 = 1;
        break;
 
+      case 'p':
+       if (vppcom_unformat_proto (&vsm->cfg.proto, optarg))
+         vtwrn ("Invalid vppcom protocol %s, defaulting to TCP", optarg);
+       break;
+
       case 'D':
        vsm->cfg.proto = VPPCOM_PROTO_UDP;
        break;
@@ -399,6 +406,10 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
       case '?':
        switch (optopt)
          {
+         case 'w':
+         case 'p':
+           vtwrn ("Option `-%c' requires an argument.", optopt);
+           break;
          default:
            if (isprint (optopt))
              vtwrn ("Unknown option `-%c'.", optopt);
@@ -428,10 +439,24 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
   vcl_test_init_endpoint_addr (vsm);
 }
 
+static void
+vts_clean_connected_listeners (vcl_test_server_worker_t * wrk,
+                              int listener_fd)
+{
+  if ((vppcom_session_n_accepted (listener_fd) == 0) &
+      vppcom_session_is_connectable_listener (listener_fd))
+    {
+      vtinf ("Connected Listener fd %x has no more sessions", listener_fd);
+      vppcom_session_close (listener_fd);
+      wrk->nfds--;
+    }
+}
+
 int
 vts_handle_cfg (vcl_test_server_worker_t * wrk, vcl_test_cfg_t * rx_cfg,
                vcl_test_server_conn_t * conn, int rx_bytes)
 {
+  int listener_fd;
   if (rx_cfg->verbose)
     {
       vtinf ("(fd %d): Received a cfg msg!", conn->fd);
@@ -469,9 +494,11 @@ vts_handle_cfg (vcl_test_server_worker_t * wrk, vcl_test_cfg_t * rx_cfg,
     case VCL_TEST_TYPE_EXIT:
       vtinf ("Session fd %d closing!", conn->fd);
       clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
+      listener_fd = vppcom_session_listener (conn->fd);
       vppcom_session_close (conn->fd);
       conn_pool_free (conn);
       wrk->nfds--;
+      vts_clean_connected_listeners (wrk, listener_fd);
       break;
 
     default:
@@ -505,7 +532,8 @@ vts_worker_init (vcl_test_server_worker_t * wrk)
     vtfail ("vppcom_session_create()", wrk->listen_fd);
 
 
-  if (vsm->cfg.proto == VPPCOM_PROTO_TLS)
+  if (vsm->cfg.proto == VPPCOM_PROTO_TLS
+      || vsm->cfg.proto == VPPCOM_PROTO_QUIC)
     {
       vppcom_session_tls_add_cert (wrk->listen_fd, vcl_test_crt_rsa,
                                   vcl_test_crt_rsa_len);
@@ -590,7 +618,7 @@ vts_worker_loop (void *arg)
   vcl_test_server_main_t *vsm = &vcl_server_main;
   vcl_test_server_worker_t *wrk = arg;
   vcl_test_server_conn_t *conn;
-  int i, rx_bytes, num_ev;
+  int i, rx_bytes, num_ev, listener_fd;
   vcl_test_cfg_t *rx_cfg;
 
   if (wrk->wrk_index)
@@ -615,8 +643,11 @@ vts_worker_loop (void *arg)
          conn = &wrk->conn_pool[wrk->wait_events[i].data.u32];
          if (wrk->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP))
            {
+             vtinf ("Closing session %d on HUP", conn->fd);
+             listener_fd = vppcom_session_listener (conn->fd);
              vppcom_session_close (conn->fd);
-             wrk->nfds -= 1;
+             wrk->nfds--;
+             vts_clean_connected_listeners (wrk, listener_fd);
              if (!wrk->nfds)
                {
                  vtinf ("All client connections closed\n");
@@ -626,7 +657,12 @@ vts_worker_loop (void *arg)
            }
          if (wrk->wait_events[i].data.u32 == ~0)
            {
-             vts_new_client (wrk);
+             vts_new_client (wrk, wrk->listen_fd);
+             continue;
+           }
+         else if (vppcom_session_is_connectable_listener (conn->fd))
+           {
+             vts_new_client (wrk, conn->fd);
              continue;
            }
 
index 544b288..d46208c 100644 (file)
@@ -167,6 +167,10 @@ typedef struct
   /* Socket configuration state */
   u8 is_vep;
   u8 is_vep_session;
+  /* VCL session index of the listening session (if any) */
+  u32 listener_index;
+  /* Accepted sessions on this listener */
+  int n_accepted_sessions;
   u8 has_rx_evt;
   u32 attr;
   u64 transport_opts;
@@ -352,6 +356,7 @@ vcl_session_alloc (vcl_worker_t * wrk)
   pool_get (wrk->sessions, s);
   memset (s, 0, sizeof (*s));
   s->session_index = s - wrk->sessions;
+  s->listener_index = VCL_INVALID_SESSION_INDEX;
   return s;
 }
 
@@ -447,6 +452,26 @@ vcl_session_table_del_listener (vcl_worker_t * wrk, u64 listener_handle)
   hash_unset (wrk->session_index_by_vpp_handles, listener_handle);
 }
 
+static inline int
+vcl_session_is_connectable_listener (vcl_worker_t * wrk,
+                                    vcl_session_t * session)
+{
+  /* Tell if we session_handle is a QUIC session.
+   * We can be in the following cases :
+   * Listen session <- QUIC session <- Stream session
+   * QUIC session <- Stream session
+   */
+  vcl_session_t *ls;
+  if (session->session_type != VPPCOM_PROTO_QUIC)
+    return 0;
+  if (session->listener_index == VCL_INVALID_SESSION_INDEX)
+    return !(session->session_state & STATE_LISTEN);
+  ls = vcl_session_get_w_handle (wrk, session->listener_index);
+  if (!ls)
+    return VPPCOM_EBADFD;
+  return ls->session_state & STATE_LISTEN;
+}
+
 static inline vcl_session_t *
 vcl_session_table_lookup_listener (vcl_worker_t * wrk, u64 handle)
 {
@@ -467,7 +492,8 @@ vcl_session_table_lookup_listener (vcl_worker_t * wrk, u64 handle)
       return 0;
     }
 
-  ASSERT (session->session_state & (STATE_LISTEN | STATE_LISTEN_NO_MQ));
+  ASSERT ((session->session_state & (STATE_LISTEN | STATE_LISTEN_NO_MQ)) ||
+         vcl_session_is_connectable_listener (wrk, session));
   return session;
 }
 
index dcbbfc4..3205a81 100644 (file)
@@ -312,7 +312,9 @@ vcl_session_accepted_handler (vcl_worker_t * wrk, session_accepted_msg_t * mp,
   session->transport.lcl_port = listen_session->transport.lcl_port;
   session->transport.lcl_ip = listen_session->transport.lcl_ip;
   session->session_type = listen_session->session_type;
-  session->is_dgram = session->session_type == VPPCOM_PROTO_UDP;
+  session->is_dgram = vcl_proto_is_dgram (session->session_type);
+  session->listener_index = listen_session->session_index;
+  listen_session->n_accepted_sessions++;
 
   VDBG (1, "session %u [0x%llx]: client accept request from %s address %U"
        " port %d queue %p!", session->session_index, mp->handle,
@@ -860,7 +862,7 @@ vppcom_session_disconnect (u32 session_handle)
 {
   vcl_worker_t *wrk = vcl_worker_get_current ();
   svm_msg_q_t *vpp_evt_q;
-  vcl_session_t *session;
+  vcl_session_t *session, *listen_session;
   vcl_session_state_t state;
   u64 vpp_handle;
 
@@ -895,6 +897,12 @@ vppcom_session_disconnect (u32 session_handle)
       vppcom_send_disconnect_session (vpp_handle);
     }
 
+  if (session->listener_index != VCL_INVALID_SESSION_INDEX)
+    {
+      listen_session = vcl_session_get (wrk, session->listener_index);
+      listen_session->n_accepted_sessions--;
+    }
+
   return VPPCOM_OK;
 }
 
@@ -1029,7 +1037,7 @@ vppcom_session_create (u8 proto, u8 is_nonblocking)
   session->session_type = proto;
   session->session_state = STATE_START;
   session->vpp_handle = ~0;
-  session->is_dgram = proto == VPPCOM_PROTO_UDP;
+  session->is_dgram = vcl_proto_is_dgram (proto);
 
   if (is_nonblocking)
     VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
@@ -1100,7 +1108,8 @@ vcl_session_cleanup (vcl_worker_t * wrk, vcl_session_t * session,
                  vppcom_retval_str (rv));
          return rv;
        }
-      else if (state & STATE_OPEN)
+      else if ((state & STATE_OPEN)
+              || (vcl_session_is_connectable_listener (wrk, session)))
        {
          rv = vppcom_session_disconnect (sh);
          if (PREDICT_FALSE (rv < 0))
@@ -1206,8 +1215,7 @@ vppcom_session_listen (uint32_t listen_sh, uint32_t q_len)
       return VPPCOM_OK;
     }
 
-  VDBG (0, "session %u [0x%llx]: sending vpp listen request...",
-       listen_sh, listen_vpp_handle);
+  VDBG (0, "session %u: sending vpp listen request...", listen_sh);
 
   /*
    * Send listen request to vpp and wait for reply
@@ -1288,16 +1296,50 @@ validate_args_session_accept_ (vcl_worker_t * wrk, vcl_session_t * ls)
       return VPPCOM_EBADFD;
     }
 
-  if (ls->session_state != STATE_LISTEN)
+  if ((ls->session_state != STATE_LISTEN)
+      && (!vcl_session_is_connectable_listener (wrk, ls)))
     {
-      VDBG (0, "ERROR: session [0x%llx]: not in listen state! state 0x%x"
-           " (%s)", ls->vpp_handle, ls->session_index, ls->session_state,
+      VDBG (0,
+           "ERROR: session [0x%llx]: not in listen state! state 0x%x"
+           " (%s)", ls->vpp_handle, ls->session_state,
            vppcom_session_state_str (ls->session_state));
       return VPPCOM_EBADFD;
     }
   return VPPCOM_OK;
 }
 
+int
+vppcom_unformat_proto (uint8_t * proto, char *proto_str)
+{
+  if (!strcmp (proto_str, "TCP"))
+    *proto = VPPCOM_PROTO_TCP;
+  else if (!strcmp (proto_str, "tcp"))
+    *proto = VPPCOM_PROTO_TCP;
+  else if (!strcmp (proto_str, "UDP"))
+    *proto = VPPCOM_PROTO_UDP;
+  else if (!strcmp (proto_str, "udp"))
+    *proto = VPPCOM_PROTO_UDP;
+  else if (!strcmp (proto_str, "UDPC"))
+    *proto = VPPCOM_PROTO_UDPC;
+  else if (!strcmp (proto_str, "udpc"))
+    *proto = VPPCOM_PROTO_UDPC;
+  else if (!strcmp (proto_str, "SCTP"))
+    *proto = VPPCOM_PROTO_SCTP;
+  else if (!strcmp (proto_str, "sctp"))
+    *proto = VPPCOM_PROTO_SCTP;
+  else if (!strcmp (proto_str, "TLS"))
+    *proto = VPPCOM_PROTO_TLS;
+  else if (!strcmp (proto_str, "tls"))
+    *proto = VPPCOM_PROTO_TLS;
+  else if (!strcmp (proto_str, "QUIC"))
+    *proto = VPPCOM_PROTO_QUIC;
+  else if (!strcmp (proto_str, "quic"))
+    *proto = VPPCOM_PROTO_QUIC;
+  else
+    return 1;
+  return 0;
+}
+
 int
 vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
                       uint32_t flags)
@@ -1432,8 +1474,7 @@ vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
       VDBG (0, "session handle %u [0x%llx]: session already "
            "connected to %s %U port %d proto %s, state 0x%x (%s)",
            session_handle, session->vpp_handle,
-           session->transport.is_ip4 ? "IPv4" : "IPv6",
-           format_ip46_address,
+           session->transport.is_ip4 ? "IPv4" : "IPv6", format_ip46_address,
            &session->transport.rmt_ip, session->transport.is_ip4 ?
            IP46_TYPE_IP4 : IP46_TYPE_IP6,
            clib_net_to_host_u16 (session->transport.rmt_port),
@@ -1450,9 +1491,10 @@ vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
     clib_memcpy_fast (&session->transport.rmt_ip.ip6, server_ep->ip,
                      sizeof (ip6_address_t));
   session->transport.rmt_port = server_ep->port;
+  session->transport_opts = VCL_INVALID_SESSION_HANDLE;
 
-  VDBG (0, "session handle %u [0x%llx]: connecting to server %s %U "
-       "port %d proto %s", session_handle, session->vpp_handle,
+  VDBG (0, "session handle %u: connecting to server %s %U "
+       "port %d proto %s", session_handle,
        session->transport.is_ip4 ? "IPv4" : "IPv6",
        format_ip46_address,
        &session->transport.rmt_ip, session->transport.is_ip4 ?
@@ -1474,6 +1516,69 @@ vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
   return rv;
 }
 
+int
+vppcom_session_stream_connect (uint32_t session_handle,
+                              uint32_t parent_session_handle)
+{
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+  vcl_session_t *session, *parent_session;
+  u32 session_index, parent_session_index;
+  int rv;
+
+  session = vcl_session_get_w_handle (wrk, session_handle);
+  if (!session)
+    return VPPCOM_EBADFD;
+  parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
+  if (!parent_session)
+    return VPPCOM_EBADFD;
+
+  session_index = session->session_index;
+  parent_session_index = parent_session->session_index;
+  if (PREDICT_FALSE (session->is_vep))
+    {
+      VDBG (0, "ERROR: cannot connect epoll session %u!",
+           session->session_index);
+      return VPPCOM_EBADFD;
+    }
+
+  if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
+    {
+      VDBG (0, "session handle %u [0x%llx]: session already "
+           "connected to session %u [0x%llx] proto %s, state 0x%x (%s)",
+           session_handle, session->vpp_handle,
+           parent_session_handle, parent_session->vpp_handle,
+           vppcom_proto_str (session->session_type), session->session_state,
+           vppcom_session_state_str (session->session_state));
+      return VPPCOM_OK;
+    }
+
+  /* Connect to quic session specifics */
+  session->transport.is_ip4 = parent_session->transport.is_ip4;
+  session->transport.rmt_ip.ip4.as_u32 = (uint32_t) 1;
+  session->transport.rmt_port = 0;
+  session->transport_opts = parent_session->vpp_handle;
+
+  VDBG (0, "session handle %u: connecting to session %u [0x%llx]",
+       session_handle, parent_session_handle, parent_session->vpp_handle);
+
+  /*
+   * Send connect request and wait for reply from vpp
+   */
+  vppcom_send_connect_sock (session);
+  rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
+                                            vcm->cfg.session_timeout);
+
+  session->listener_index = parent_session_index;
+  parent_session = vcl_session_get_w_handle (wrk, parent_session_handle);
+  parent_session->n_accepted_sessions++;
+
+  session = vcl_session_get (wrk, session_index);
+  VDBG (0, "session %u [0x%llx]: connect %s!", session->session_index,
+       session->vpp_handle, rv ? "failed" : "succeeded");
+
+  return rv;
+}
+
 static u8
 vcl_is_rx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
 {
@@ -3379,6 +3484,43 @@ vppcom_worker_mqs_epfd (void)
   return wrk->mqs_epfd;
 }
 
+int
+vppcom_session_is_connectable_listener (uint32_t session_handle)
+{
+  vcl_session_t *session;
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+  session = vcl_session_get_w_handle (wrk, session_handle);
+  if (!session)
+    return VPPCOM_EBADFD;
+  return vcl_session_is_connectable_listener (wrk, session);
+}
+
+int
+vppcom_session_listener (uint32_t session_handle)
+{
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+  vcl_session_t *listen_session, *session;
+  session = vcl_session_get_w_handle (wrk, session_handle);
+  if (!session)
+    return VPPCOM_EBADFD;
+  if (session->listener_index == VCL_INVALID_SESSION_INDEX)
+    return VPPCOM_EBADFD;
+  listen_session = vcl_session_get_w_handle (wrk, session->listener_index);
+  if (!listen_session)
+    return VPPCOM_EBADFD;
+  return vcl_session_handle (listen_session);
+}
+
+int
+vppcom_session_n_accepted (uint32_t session_handle)
+{
+  vcl_worker_t *wrk = vcl_worker_get_current ();
+  vcl_session_t *session = vcl_session_get_w_handle (wrk, session_handle);
+  if (!session)
+    return VPPCOM_EBADFD;
+  return session->n_accepted_sessions;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 6dfdd26..b05eae7 100644 (file)
@@ -51,7 +51,8 @@ typedef enum
   VPPCOM_PROTO_SCTP,
   VPPCOM_PROTO_NONE,
   VPPCOM_PROTO_TLS,
-  VPPCOM_PROTO_UDPC
+  VPPCOM_PROTO_UDPC,
+  VPPCOM_PROTO_QUIC,
 } vppcom_proto_t;
 
 static inline char *
@@ -76,6 +77,9 @@ vppcom_proto_str (vppcom_proto_t proto)
     case VPPCOM_PROTO_UDPC:
       proto_str = "UDPC";
       break;
+    case VPPCOM_PROTO_QUIC:
+      proto_str = "QUIC";
+      break;
     default:
       proto_str = "UNKNOWN";
       break;
@@ -83,6 +87,12 @@ vppcom_proto_str (vppcom_proto_t proto)
   return proto_str;
 }
 
+static inline int
+vcl_proto_is_dgram (uint8_t proto)
+{
+  return proto == VPPCOM_PROTO_UDP || proto == VPPCOM_PROTO_UDPC;
+}
+
 typedef enum
 {
   VPPCOM_IS_IP6 = 0,
@@ -95,6 +105,7 @@ typedef struct vppcom_endpt_t_
   uint8_t is_ip4;
   uint8_t *ip;
   uint16_t port;
+  uint64_t transport_opts;
 } vppcom_endpt_t;
 
 typedef uint32_t vcl_session_handle_t;
@@ -254,6 +265,8 @@ extern int vppcom_session_accept (uint32_t session_handle,
 
 extern int vppcom_session_connect (uint32_t session_handle,
                                   vppcom_endpt_t * server_ep);
+extern int vppcom_session_stream_connect (uint32_t session_handle,
+                                         uint32_t parent_session_handle);
 extern int vppcom_session_read (uint32_t session_handle, void *buf, size_t n);
 extern int vppcom_session_write (uint32_t session_handle, void *buf,
                                 size_t n);
@@ -294,6 +307,10 @@ extern int vppcom_session_tls_add_key (uint32_t session_handle, char *key,
                                       uint32_t key_len);
 extern int vppcom_data_segment_copy (void *buf, vppcom_data_segments_t ds,
                                     uint32_t max_bytes);
+extern int vppcom_unformat_proto (uint8_t * proto, char *proto_str);
+extern int vppcom_session_is_connectable_listener (uint32_t session_handle);
+extern int vppcom_session_listener (uint32_t session_handle);
+extern int vppcom_session_n_accepted (uint32_t session_handle);
 
 /**
  * Request from application to register a new worker