VCL API for external callback for listener/connect event

Change-Id: Ic59355683b581945d10a2df97d9b2deae87a998e
Signed-off-by: Keith Burns (alagalah) <alagalah@gmail.com>
diff --git a/src/vcl/test_vcl_listener_client.c b/src/vcl/test_vcl_listener_client.c
new file mode 100644
index 0000000..dcf93cd
--- /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 <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <vcl/vppcom.h>
+
+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 0000000..bebef25
--- /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 <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+
+#include <vcl/vppcom.h>
+#include <unistd.h>
+
+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 64f55b9..f6e20de 100644
--- a/src/vcl/vcl_event.c
+++ b/src/vcl/vcl_event.c
@@ -96,7 +96,7 @@
 
 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 @@
   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 @@
 
   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 a2e247e..f2a85a0 100644
--- a/src/vcl/vcl_event.h
+++ b/src/vcl/vcl_event.h
@@ -53,6 +53,7 @@
   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 @@
  * @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 c0b09e8..58de9ae 100644
--- a/src/vcl/vppcom.c
+++ b/src/vcl/vppcom.c
@@ -203,6 +203,13 @@
   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 @@
 
 }
 
+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 (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)
 {
@@ -2682,6 +2734,39 @@
 }
 
 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)
 {
   u32 listen_session_index = listen_session - vcm->sessions;
@@ -2752,10 +2837,8 @@
   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 (&reg->handler_lock);
   while (!ev)
     {
@@ -3477,7 +3560,8 @@
                       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 @@
 	  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 9d09f06..34a69b2 100644
--- a/src/vcl/vppcom.h
+++ b/src/vcl/vppcom.h
@@ -210,6 +210,46 @@
   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_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);