summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>2019-05-16 14:38:44 +0200
committerFlorin Coras <florin.coras@gmail.com>2019-07-18 18:19:05 +0000
commit9fd996275c745faec2843cf3a8b1d15d6f8c9dab (patch)
tree01bd59cd9deea4994a33bce8bb4a2a7d7fa5c283 /src
parentcef02be220eff4aa32ec7ff56b1e0a552faa1280 (diff)
vcl: add QUIC support
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>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test.h31
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test_client.c209
-rw-r--r--src/plugins/hs_apps/vcl/vcl_test_server.c52
-rw-r--r--src/vcl/vcl_private.h28
-rw-r--r--src/vcl/vppcom.c168
-rw-r--r--src/vcl/vppcom.h19
6 files changed, 423 insertions, 84 deletions
diff --git a/src/plugins/hs_apps/vcl/vcl_test.h b/src/plugins/hs_apps/vcl/vcl_test.h
index ab05f7ae9cf..7586e29749a 100644
--- a/src/plugins/hs_apps/vcl/vcl_test.h
+++ b/src/plugins/hs_apps/vcl/vcl_test.h
@@ -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);
diff --git a/src/plugins/hs_apps/vcl/vcl_test_client.c b/src/plugins/hs_apps/vcl/vcl_test_client.c
index 1ead146aef5..b9bdd6eed55 100644
--- a/src/plugins/hs_apps/vcl/vcl_test_client.c
+++ b/src/plugins/hs_apps/vcl/vcl_test_client.c
@@ -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;
@@ -116,6 +119,105 @@ vtc_cfg_sync (vcl_test_session_t * ts)
}
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)
{
vcl_test_client_main_t *vcm = &vcl_client_main;
@@ -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;
diff --git a/src/plugins/hs_apps/vcl/vcl_test_server.c b/src/plugins/hs_apps/vcl/vcl_test_server.c
index 62292adae5f..be225fa8c9e 100644
--- a/src/plugins/hs_apps/vcl/vcl_test_server.c
+++ b/src/plugins/hs_apps/vcl/vcl_test_server.c
@@ -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;
}
diff --git a/src/vcl/vcl_private.h b/src/vcl/vcl_private.h
index 544b2880fe1..d46208cd035 100644
--- a/src/vcl/vcl_private.h
+++ b/src/vcl/vcl_private.h
@@ -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;
}
diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c
index dcbbfc44a39..3205a812ce4 100644
--- a/src/vcl/vppcom.c
+++ b/src/vcl/vppcom.c
@@ -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,10 +1296,12 @@ 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;
}
@@ -1299,6 +1309,38 @@ validate_args_session_accept_ (vcl_worker_t * wrk, vcl_session_t * ls)
}
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
*
diff --git a/src/vcl/vppcom.h b/src/vcl/vppcom.h
index 6dfdd267ac7..b05eae7150c 100644
--- a/src/vcl/vppcom.h
+++ b/src/vcl/vppcom.h
@@ -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