From 9fd996275c745faec2843cf3a8b1d15d6f8c9dab Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Thu, 16 May 2019 14:38:44 +0200 Subject: [PATCH] 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 --- src/plugins/hs_apps/vcl/vcl_test.h | 31 ++--- src/plugins/hs_apps/vcl/vcl_test_client.c | 209 ++++++++++++++++++++++++------ src/plugins/hs_apps/vcl/vcl_test_server.c | 52 ++++++-- src/vcl/vcl_private.h | 28 +++- src/vcl/vppcom.c | 168 ++++++++++++++++++++++-- src/vcl/vppcom.h | 19 ++- 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; @@ -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 Write test results to .\n" " -X Exit after running test.\n" + " -p Use 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 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 Use N sessions.\n" + " -s Use N sessions.\n" + " -q 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 Number of workers\n" + " -p Use 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,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 * 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 -- 2.16.6