From 0d2b0d5497b61afb5c964373c7bed974d78762a0 Mon Sep 17 00:00:00 2001 From: "Keith Burns (alagalah)" Date: Tue, 6 Mar 2018 15:55:22 -0800 Subject: [PATCH] VCL API for external callback for listener/connect event Change-Id: Ic59355683b581945d10a2df97d9b2deae87a998e Signed-off-by: Keith Burns (alagalah) --- src/vcl.am | 12 +++- src/vcl/test_vcl_listener_client.c | 58 +++++++++++++++++ src/vcl/test_vcl_listener_server.c | 102 ++++++++++++++++++++++++++++++ src/vcl/vcl_event.c | 5 +- src/vcl/vcl_event.h | 5 +- src/vcl/vppcom.c | 123 +++++++++++++++++++++++++++++++------ src/vcl/vppcom.h | 41 +++++++++++++ 7 files changed, 323 insertions(+), 23 deletions(-) create mode 100644 src/vcl/test_vcl_listener_client.c create mode 100644 src/vcl/test_vcl_listener_server.c diff --git a/src/vcl.am b/src/vcl.am index 9f1325ecaa2..89e18416b1e 100644 --- a/src/vcl.am +++ b/src/vcl.am @@ -49,11 +49,21 @@ noinst_PROGRAMS += \ vcl_test_server \ vcl_test_client \ sock_test_server \ - sock_test_client + sock_test_client \ + test_vcl_listener_server \ + test_vcl_listener_client + + +test_vcl_listener_server_SOURCES = vcl/test_vcl_listener_server.c +test_vcl_listener_server_LDADD = libvppcom.la + +test_vcl_listener_client_SOURCES = vcl/test_vcl_listener_client.c +test_vcl_listener_client_LDADD = libvppcom.la vcl_test_server_SOURCES = vcl/vcl_test_server.c vcl_test_server_LDADD = libvppcom.la + vcl_test_client_SOURCES = vcl/vcl_test_client.c # Link libvcl_ldpreload.la instead of vppcom.la diff --git a/src/vcl/test_vcl_listener_client.c b/src/vcl/test_vcl_listener_client.c new file mode 100644 index 00000000000..dcf93cdef8b --- /dev/null +++ b/src/vcl/test_vcl_listener_client.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +int main(){ + int client_session; + char buffer[1024]; + struct sockaddr_in server_address; + vppcom_endpt_t endpt; + int rv; + + rv = vppcom_app_create ("test_vcl_listener_client"); + if (rv) return rv; + + client_session = vppcom_session_create(VPPCOM_PROTO_TCP, 0); + + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_port = htons(9995); + server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); + + endpt.is_ip4 = (server_address.sin_family == AF_INET); + endpt.ip = (uint8_t *) & server_address.sin_addr; + endpt.port = (uint16_t) server_address.sin_port; + + + vppcom_session_connect(client_session, &endpt); + + /*---- Read the message from the server into the buffer ----*/ + vppcom_session_read (client_session, buffer, 1024); + + /*---- Print the received message ----*/ + printf("Data received: %s",buffer); + + printf("Press ENTER key to Continue\n"); + getchar(); + + return 0; +} diff --git a/src/vcl/test_vcl_listener_server.c b/src/vcl/test_vcl_listener_server.c new file mode 100644 index 00000000000..bebef250f22 --- /dev/null +++ b/src/vcl/test_vcl_listener_server.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +char MESSAGE[] = "Hello, World!\n"; + +static const int PORT = 9995; + +void +listener_cb (uint32_t new_session_index, vppcom_endpt_t *ep, void *stuff) +{ + + vppcom_session_write (new_session_index, &MESSAGE, sizeof (MESSAGE)); + printf ("\n Heard from port: %d\n", ep->port); +} + + +typedef struct vppcomm_listener_main_ +{ + int new_fd; + + struct event *event; + +} vppcomm_listener_main_t; + +vppcomm_listener_main_t _vlm_main; +vppcomm_listener_main_t *vlm = &_vlm_main; + + +int +main (int argc, char **argv) +{ + + int rv; + struct sockaddr_in sin; + uint32_t listen_fd; + vppcom_endpt_t endpt; + + //Address stuff + memset (&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (PORT); + //sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + + endpt.is_ip4 = (sin.sin_family == AF_INET); + endpt.ip = (uint8_t *) & sin.sin_addr; + endpt.port = (uint16_t) sin.sin_port; + + //VCL stuff + rv = vppcom_app_create ("test_vcl_listener_server"); + if (rv) return rv; + + listen_fd = vppcom_session_create (VPPCOM_PROTO_TCP, + 0 /* is_nonblocking */ ); + + rv = vppcom_session_bind (listen_fd, &endpt); + + //Make a listener and dispatch + rv = vppcom_session_register_listener (listen_fd, listener_cb, 0, + 0, 0, &MESSAGE); + + if (rv) + { + fprintf (stderr, "Could not create a listener!\n"); + return 1; + } + + while (1) + { + sleep (3); + } + + printf ("done\n"); + return 0; +} + + diff --git a/src/vcl/vcl_event.c b/src/vcl/vcl_event.c index 64f55b9fb0b..f6e20de2769 100644 --- a/src/vcl/vcl_event.c +++ b/src/vcl/vcl_event.c @@ -96,7 +96,7 @@ vce_get_event_handler (vce_event_thread_t *evt, vce_event_key_t *evk) vce_event_handler_reg_t * vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk, - vce_event_callback_t cb) + vce_event_callback_t cb, void *cb_args) { vce_event_handler_reg_t *handler; vce_event_handler_reg_t *old_handler = 0; @@ -135,6 +135,7 @@ vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk, handler->replaced_handler_idx = (p) ? p[0] : ~0; handler->ev_idx = ~0; //This will be set by the event thread if event happens handler->evk = evk->as_u64; + handler->handler_fn_args = cb_args; hash_set (evt->handlers_index_by_event_key, evk->as_u64, handler_index); @@ -275,4 +276,4 @@ vce_start_event_thread (vce_event_thread_t *evt, u8 max_events) return pthread_create (&(evt->thread), NULL /* attr */ , vce_event_thread_fn, evt); -} \ No newline at end of file +} diff --git a/src/vcl/vcl_event.h b/src/vcl/vcl_event.h index a2e247e8d7c..f2a85a0f1d2 100644 --- a/src/vcl/vcl_event.h +++ b/src/vcl/vcl_event.h @@ -53,6 +53,7 @@ typedef struct vce_event_handler_reg_ u32 ev_idx; u64 evk; //Event key u32 replaced_handler_idx; + void *handler_fn_args; } vce_event_handler_reg_t; typedef struct vce_event_thread_ @@ -121,12 +122,14 @@ vce_event_handler_reg_t * vce_get_event_handler (vce_event_thread_t *evt, * @param evk - vce_event_key_t current an eventID from enum in consumer and * sessionID * @param cb - vce_event_callback_t function to handle event + * @param cb_args - args that the callback needs passed back to it. * @return vce_handler_reg_t - the function that needs event notification * needs to block on a condvar mutex to reduce spin. That is in here. */ vce_event_handler_reg_t * vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk, - vce_event_callback_t cb); + vce_event_callback_t cb, + void *cb_args); /** * @brief vce_unregister_handler diff --git a/src/vcl/vppcom.c b/src/vcl/vppcom.c index c0b09e833d7..58de9aeea22 100644 --- a/src/vcl/vppcom.c +++ b/src/vcl/vppcom.c @@ -203,6 +203,13 @@ typedef struct vce_event_connect_request_ u32 accepted_session_index; } vce_event_connect_request_t; +typedef struct vppcom_session_listener +{ + vppcom_session_listener_cb user_cb; + vppcom_session_listener_errcb user_errcb; + void *user_cb_data; +} vppcom_session_listener_t; + typedef struct vppcom_main_t_ { u8 init; @@ -436,10 +443,69 @@ write_elog (void) } +static inline void +vppcom_send_accept_session_reply (u64 handle, u32 context, int retval) +{ + vl_api_accept_session_reply_t *rmp; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_ACCEPT_SESSION_REPLY); + rmp->retval = htonl (retval); + rmp->context = context; + rmp->handle = handle; + vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp); +} + /* * VPPCOM Event Functions */ +void +vce_registered_listener_connect_handler_fn (void *arg) +{ + vce_event_handler_reg_t *reg = (vce_event_handler_reg_t *) arg; + vce_event_connect_request_t *ecr; + vce_event_t *ev; + vppcom_endpt_t ep; + + session_t *new_session; + int rv; + + vppcom_session_listener_t *session_listener = + (vppcom_session_listener_t *) reg->handler_fn_args; + + ev = vce_get_event_from_index (&vcm->event_thread, reg->ev_idx); + + ecr = (vce_event_connect_request_t *) ev->data; + VCL_LOCK_AND_GET_SESSION (ecr->accepted_session_index, &new_session); + + + ep.is_ip4 = new_session->peer_addr.is_ip4; + ep.port = new_session->peer_port; + if (new_session->peer_addr.is_ip4) + clib_memcpy (&ep.ip, &new_session->peer_addr.ip46.ip4, + sizeof (ip4_address_t)); + else + clib_memcpy (&ep.ip, &new_session->peer_addr.ip46.ip6, + sizeof (ip6_address_t)); + + vppcom_send_accept_session_reply (new_session->vpp_handle, + new_session->client_context, + 0 /* retval OK */ ); + clib_spinlock_unlock (&vcm->sessions_lockp); + + (session_listener->user_cb) (ecr->accepted_session_index, &ep, + session_listener->user_cb_data); + + /*TODO - Unregister check in close for this listener */ + + return; + +done: + ASSERT (0); // If we can't get a lock or accepted session fails, lets blow up. +} + /** * * @brief vce_connect_request_handler_fn * - used for listener sessions @@ -1250,20 +1316,6 @@ format_ip46_address (u8 * s, va_list * args) format (s, "%U", format_ip6_address, &ip46->ip6); } -static inline void -vppcom_send_accept_session_reply (u64 handle, u32 context, int retval) -{ - vl_api_accept_session_reply_t *rmp; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_ACCEPT_SESSION_REPLY); - rmp->retval = htonl (retval); - rmp->context = context; - rmp->handle = handle; - vl_msg_api_send_shmem (vcm->vl_input_queue, (u8 *) & rmp); -} - static void vl_api_accept_session_t_handler (vl_api_accept_session_t * mp) { @@ -2681,6 +2733,39 @@ done: return rv; } +int +vppcom_session_register_listener (uint32_t session_index, + vppcom_session_listener_cb cb, + vppcom_session_listener_errcb + errcb, uint8_t flags, int q_len, void *ptr) +{ + int rv = VPPCOM_OK; + vce_event_key_t evk; + vppcom_session_listener_t *listener_args; + + rv = vppcom_session_listen (session_index, q_len); + if (rv) + { + goto done; + } + + + /* Register handler for connect_request event on listen_session_index */ + listener_args = clib_mem_alloc (sizeof (vppcom_session_listener_t)); + listener_args->user_cb = cb; + listener_args->user_cb_data = ptr; + listener_args->user_errcb = errcb; + + evk.session_index = session_index; + evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED; + (void) vce_register_handler (&vcm->event_thread, &evk, + vce_registered_listener_connect_handler_fn, + listener_args); + +done: + return rv; +} + int validate_args_session_accept_ (session_t * listen_session) { @@ -2752,10 +2837,8 @@ vppcom_session_accept (uint32_t listen_session_index, vppcom_endpt_t * ep, evk.session_index = listen_session_index; evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED; reg = vce_register_handler (&vcm->event_thread, &evk, - vce_connect_request_handler_fn); - + vce_connect_request_handler_fn, 0); ev = vce_get_event_from_index (&vcm->event_thread, reg->ev_idx); - pthread_mutex_lock (®->handler_lock); while (!ev) { @@ -3477,7 +3560,8 @@ vppcom_select (unsigned long n_bits, unsigned long *read_map, reg = vce_get_event_handler (&vcm->event_thread, &evk); if (!reg) reg = vce_register_handler (&vcm->event_thread, &evk, - vce_poll_wait_connect_request_handler_fn); + vce_poll_wait_connect_request_handler_fn, + 0 /* No callback args */); rv = vppcom_session_read_ready (session, session_index); if (rv > 0) { @@ -3796,7 +3880,8 @@ vppcom_epoll_ctl (uint32_t vep_idx, int op, uint32_t session_index, evk.eid = VCL_EVENT_CONNECT_REQ_ACCEPTED; vep_session->poll_reg = vce_register_handler (&vcm->event_thread, &evk, - vce_poll_wait_connect_request_handler_fn); + vce_poll_wait_connect_request_handler_fn, + 0 /* No callback args */ ); } if (VPPCOM_DEBUG > 1) clib_warning ("VCL<%d>: EPOLL_CTL_ADD: vep_idx %u, " diff --git a/src/vcl/vppcom.h b/src/vcl/vppcom.h index 9d09f060b8b..34a69b2c2ec 100644 --- a/src/vcl/vppcom.h +++ b/src/vcl/vppcom.h @@ -210,6 +210,46 @@ vppcom_retval_str (int retval) return st; } +/** + * User registered callback for when connection arrives on listener created + * with vppcom_session_register_listener() + * @param uint32_t - newly accepted session_index + * @param vppcom_endpt_t* - ip/port information of remote + * @param void* - user passed arg to pass back + */ +typedef void (*vppcom_session_listener_cb) (uint32_t, vppcom_endpt_t *, + void *); + +/** + * User registered ERROR callback for any errors associated with + * handling vppcom_session_register_listener() and connections + * @param void* - user passed arg to pass back + */ +typedef void (*vppcom_session_listener_errcb) (void *); + +/** + * @brief vppcom_session_register_listener accepts a bound session_index, and + * listens for connections. + * + * On successful connection, calls registered callback (cb) with new + * session_index. + * + * On error, calls registered error callback (errcb). + * + * @param session_index - bound session_index to create listener on + * @param cb - on new accepted session callback + * @param errcb - on failure callback + * @param flags - placeholder for future use. Must be ZERO + * @param q_len - max listener connection backlog + * @param ptr - user data + * @return + */ +extern int vppcom_session_register_listener (uint32_t session_index, + vppcom_session_listener_cb cb, + vppcom_session_listener_errcb + errcb, uint8_t flags, int q_len, + void *ptr); + /* TBD: make these constructor/destructor function */ extern int vppcom_app_create (char *app_name); extern void vppcom_app_destroy (void); @@ -219,6 +259,7 @@ extern int vppcom_session_close (uint32_t session_index); extern int vppcom_session_bind (uint32_t session_index, vppcom_endpt_t * ep); extern int vppcom_session_listen (uint32_t session_index, uint32_t q_len); + extern int vppcom_session_accept (uint32_t session_index, vppcom_endpt_t * client_ep, uint32_t flags); -- 2.16.6