vcl: if the ldp user send buf with 0 len, it will assert failed.
[vpp.git] / src / vcl / vppcom.c
index d449fb9..0e5d4fa 100644 (file)
@@ -197,6 +197,99 @@ format_ip46_address (u8 * s, va_list * args)
  * VPPCOM Utility Functions
  */
 
+static void
+vcl_send_session_listen (vcl_worker_t * wrk, vcl_session_t * s)
+{
+  app_session_evt_t _app_evt, *app_evt = &_app_evt;
+  session_listen_msg_t *mp;
+  svm_msg_q_t *mq;
+
+  mq = vcl_worker_ctrl_mq (wrk);
+  app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_LISTEN);
+  mp = (session_listen_msg_t *) app_evt->evt->data;
+  memset (mp, 0, sizeof (*mp));
+  mp->client_index = wrk->my_client_index;
+  mp->context = s->session_index;
+  mp->wrk_index = wrk->vpp_wrk_index;
+  mp->is_ip4 = s->transport.is_ip4;
+  clib_memcpy_fast (&mp->ip, &s->transport.lcl_ip, sizeof (mp->ip));
+  mp->port = s->transport.lcl_port;
+  mp->proto = s->session_type;
+  app_send_ctrl_evt_to_vpp (mq, app_evt);
+}
+
+static void
+vcl_send_session_connect (vcl_worker_t * wrk, vcl_session_t * s)
+{
+  app_session_evt_t _app_evt, *app_evt = &_app_evt;
+  session_connect_msg_t *mp;
+  svm_msg_q_t *mq;
+
+  mq = vcl_worker_ctrl_mq (wrk);
+  app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_CONNECT);
+  mp = (session_connect_msg_t *) app_evt->evt->data;
+  memset (mp, 0, sizeof (*mp));
+  mp->client_index = wrk->my_client_index;
+  mp->context = s->session_index;
+  mp->wrk_index = wrk->vpp_wrk_index;
+  mp->is_ip4 = s->transport.is_ip4;
+  mp->parent_handle = s->parent_handle;
+  clib_memcpy_fast (&mp->ip, &s->transport.rmt_ip, sizeof (mp->ip));
+  clib_memcpy_fast (&mp->lcl_ip, &s->transport.lcl_ip, sizeof (mp->lcl_ip));
+  mp->port = s->transport.rmt_port;
+  mp->proto = s->session_type;
+  app_send_ctrl_evt_to_vpp (mq, app_evt);
+}
+
+void
+vcl_send_session_unlisten (vcl_worker_t * wrk, vcl_session_t * s)
+{
+  app_session_evt_t _app_evt, *app_evt = &_app_evt;
+  session_unlisten_msg_t *mp;
+  svm_msg_q_t *mq;
+
+  mq = vcl_worker_ctrl_mq (wrk);
+  app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_UNLISTEN);
+  mp = (session_unlisten_msg_t *) app_evt->evt->data;
+  memset (mp, 0, sizeof (*mp));
+  mp->client_index = wrk->my_client_index;
+  mp->wrk_index = wrk->vpp_wrk_index;
+  mp->handle = s->vpp_handle;
+  mp->context = wrk->wrk_index;
+  app_send_ctrl_evt_to_vpp (mq, app_evt);
+}
+
+static void
+vcl_send_session_disconnect (vcl_worker_t * wrk, vcl_session_t * s)
+{
+  app_session_evt_t _app_evt, *app_evt = &_app_evt;
+  session_disconnect_msg_t *mp;
+  svm_msg_q_t *mq;
+
+  /* Send to thread that owns the session */
+  mq = s->vpp_evt_q;
+  app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_DISCONNECT);
+  mp = (session_disconnect_msg_t *) app_evt->evt->data;
+  memset (mp, 0, sizeof (*mp));
+  mp->client_index = wrk->my_client_index;
+  mp->handle = s->vpp_handle;
+  app_send_ctrl_evt_to_vpp (mq, app_evt);
+}
+
+static void
+vcl_send_app_detach (vcl_worker_t * wrk)
+{
+  app_session_evt_t _app_evt, *app_evt = &_app_evt;
+  session_app_detach_msg_t *mp;
+  svm_msg_q_t *mq;
+
+  mq = vcl_worker_ctrl_mq (wrk);
+  app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_APP_DETACH);
+  mp = (session_app_detach_msg_t *) app_evt->evt->data;
+  memset (mp, 0, sizeof (*mp));
+  mp->client_index = wrk->my_client_index;
+  app_send_ctrl_evt_to_vpp (mq, app_evt);
+}
 
 static void
 vcl_send_session_accepted_reply (svm_msg_q_t * mq, u32 context,
@@ -356,19 +449,23 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
     {
       VDBG (0, "ERROR: session index %u: connect failed! %U",
            session_index, format_api_error, ntohl (mp->retval));
-      session->session_state = STATE_FAILED;
+      session->session_state = STATE_FAILED | STATE_DISCONNECT;
       session->vpp_handle = mp->handle;
       return session_index;
     }
 
+  session->vpp_handle = mp->handle;
+  session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
+                                        svm_msg_q_t *);
   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
   if (vcl_wait_for_segment (mp->segment_handle))
     {
       VDBG (0, "segment for session %u couldn't be mounted!",
            session->session_index);
-      session->session_state = STATE_FAILED;
-      return VCL_INVALID_SESSION_INDEX;
+      session->session_state = STATE_FAILED | STATE_DISCONNECT;
+      vcl_send_session_disconnect (wrk, session);
+      return session_index;
     }
 
   rx_fifo->client_session_index = session_index;
@@ -376,8 +473,6 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
   rx_fifo->client_thread_index = vcl_get_worker_index ();
   tx_fifo->client_thread_index = vcl_get_worker_index ();
 
-  session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
-                                        svm_msg_q_t *);
   vpp_wrk_index = tx_fifo->master_thread_index;
   vec_validate (wrk->vpp_event_queues, vpp_wrk_index);
   wrk->vpp_event_queues[vpp_wrk_index] = session->vpp_evt_q;
@@ -390,14 +485,14 @@ vcl_session_connected_handler (vcl_worker_t * wrk,
        {
          VDBG (0, "ct segment for session %u couldn't be mounted!",
                session->session_index);
-         session->session_state = STATE_FAILED;
-         return VCL_INVALID_SESSION_INDEX;
+         session->session_state = STATE_FAILED | STATE_DISCONNECT;
+         vcl_send_session_disconnect (wrk, session);
+         return session_index;
        }
     }
 
   session->rx_fifo = rx_fifo;
   session->tx_fifo = tx_fifo;
-  session->vpp_handle = mp->handle;
   session->vpp_thread_index = rx_fifo->master_thread_index;
   session->transport.is_ip4 = mp->lcl.is_ip4;
   clib_memcpy_fast (&session->transport.lcl_ip, &mp->lcl.ip,
@@ -827,7 +922,6 @@ vppcom_session_unbind (u32 session_handle)
   session_accepted_msg_t *accepted_msg;
   vcl_session_t *session = 0;
   vcl_session_msg_t *evt;
-  u64 vpp_handle;
 
   session = vcl_session_get_w_handle (wrk, session_handle);
   if (!session)
@@ -845,14 +939,14 @@ vppcom_session_unbind (u32 session_handle)
     }
   clib_fifo_free (session->accept_evts_fifo);
 
-  vpp_handle = session->vpp_handle;
-  session->vpp_handle = ~0;
-  session->session_state = STATE_DISCONNECT;
+  vcl_send_session_unlisten (wrk, session);
 
   VDBG (1, "session %u [0x%llx]: sending unbind!", session->session_index,
-       vpp_handle);
+       session->vpp_handle);
   vcl_evt (VCL_EVT_UNBIND, session);
-  vppcom_send_unbind_sock (wrk, vpp_handle);
+
+  session->vpp_handle = ~0;
+  session->session_state = STATE_DISCONNECT;
 
   return VPPCOM_OK;
 }
@@ -894,7 +988,7 @@ vppcom_session_disconnect (u32 session_handle)
     {
       VDBG (1, "session %u [0x%llx]: sending disconnect...",
            session->session_index, vpp_handle);
-      vppcom_send_disconnect_session (vpp_handle);
+      vcl_send_session_disconnect (wrk, session);
     }
 
   if (session->listener_index != VCL_INVALID_SESSION_INDEX)
@@ -922,7 +1016,7 @@ vppcom_app_exit (void)
   vcl_set_worker_index (~0);
   vcl_elog_stop (vcm);
   if (vec_len (vcm->workers) == 1)
-    vl_client_disconnect_from_vlib ();
+    vppcom_disconnect_from_vpp ();
   else
     vl_client_send_disconnect (1 /* vpp should cleanup */ );
 }
@@ -1005,7 +1099,7 @@ vppcom_app_destroy (void)
 
   if (pool_elts (vcm->workers) == 1)
     {
-      vppcom_app_send_detach ();
+      vcl_send_app_detach (vcl_worker_get_current ());
       orig_app_timeout = vcm->cfg.app_timeout;
       vcm->cfg.app_timeout = 2.0;
       rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
@@ -1220,7 +1314,7 @@ vppcom_session_listen (uint32_t listen_sh, uint32_t q_len)
   /*
    * Send listen request to vpp and wait for reply
    */
-  vppcom_send_bind_sock (listen_session);
+  vcl_send_session_listen (wrk, listen_session);
   rv = vppcom_wait_for_session_state_change (listen_session->session_index,
                                             STATE_LISTEN,
                                             vcm->cfg.session_timeout);
@@ -1256,7 +1350,8 @@ vppcom_session_tls_add_cert (uint32_t session_handle, char *cert,
    * Send listen request to vpp and wait for reply
    */
   vppcom_send_application_tls_cert_add (session, cert, cert_len);
-
+  vcm->app_state = STATE_APP_ADDING_TLS_DATA;
+  vcl_wait_for_app_state_change (STATE_APP_READY);
   return VPPCOM_OK;
 
 }
@@ -1276,14 +1371,10 @@ vppcom_session_tls_add_key (uint32_t session_handle, char *key,
   if (key_len == 0 || key_len == ~0)
     return VPPCOM_EBADFD;
 
-  /*
-   * Send listen request to vpp and wait for reply
-   */
   vppcom_send_application_tls_key_add (session, key, key_len);
-
+  vcm->app_state = STATE_APP_ADDING_TLS_DATA;
+  vcl_wait_for_app_state_change (STATE_APP_READY);
   return VPPCOM_OK;
-
-
 }
 
 static int
@@ -1373,11 +1464,11 @@ vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
 
   is_nonblocking = VCL_SESS_ATTR_TEST (listen_session->attr,
                                       VCL_SESS_ATTR_NONBLOCK);
-  if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
-    return VPPCOM_EAGAIN;
-
   while (1)
     {
+      if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
+       return VPPCOM_EAGAIN;
+
       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_WAIT, 0))
        return VPPCOM_EAGAIN;
 
@@ -1449,6 +1540,25 @@ handle:
   return vcl_session_handle (client_session);
 }
 
+static void
+vcl_ip_copy_from_ep (ip46_address_t * ip, vppcom_endpt_t * ep)
+{
+  if (ep->is_ip4)
+    clib_memcpy_fast (&ip->ip4, ep->ip, sizeof (ip4_address_t));
+  else
+    clib_memcpy_fast (&ip->ip6, ep->ip, sizeof (ip6_address_t));
+}
+
+void
+vcl_ip_copy_to_ep (ip46_address_t * ip, vppcom_endpt_t * ep, u8 is_ip4)
+{
+  ep->is_ip4 = is_ip4;
+  if (is_ip4)
+    clib_memcpy_fast (ep->ip, &ip->ip4, sizeof (ip4_address_t));
+  else
+    clib_memcpy_fast (ep->ip, &ip->ip6, sizeof (ip6_address_t));
+}
+
 int
 vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
 {
@@ -1484,12 +1594,7 @@ vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
     }
 
   session->transport.is_ip4 = server_ep->is_ip4;
-  if (session->transport.is_ip4)
-    clib_memcpy_fast (&session->transport.rmt_ip.ip4, server_ep->ip,
-                     sizeof (ip4_address_t));
-  else
-    clib_memcpy_fast (&session->transport.rmt_ip.ip6, server_ep->ip,
-                     sizeof (ip6_address_t));
+  vcl_ip_copy_from_ep (&session->transport.rmt_ip, server_ep);
   session->transport.rmt_port = server_ep->port;
   session->parent_handle = VCL_INVALID_SESSION_HANDLE;
 
@@ -1502,10 +1607,14 @@ vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
        clib_net_to_host_u16 (session->transport.rmt_port),
        vppcom_proto_str (session->session_type));
 
+  vcl_send_session_connect (wrk, session);
+
+  if (VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK))
+    return VPPCOM_EINPROGRESS;
+
   /*
-   * Send connect request and wait for reply from vpp
+   * Wait for reply from vpp if blocking
    */
-  vppcom_send_connect_sock (session);
   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
                                             vcm->cfg.session_timeout);
 
@@ -1564,7 +1673,7 @@ vppcom_session_stream_connect (uint32_t session_handle,
   /*
    * Send connect request and wait for reply from vpp
    */
-  vppcom_send_connect_sock (session);
+  vcl_send_session_connect (wrk, session);
   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
                                             vcm->cfg.session_timeout);
 
@@ -1788,7 +1897,7 @@ vppcom_session_write_inline (uint32_t session_handle, void *buf, size_t n,
   svm_msg_q_t *mq;
   u8 is_ct;
 
-  if (PREDICT_FALSE (!buf))
+  if (PREDICT_FALSE (!buf || n == 0))
     return VPPCOM_EINVAL;
 
   s = vcl_session_get_w_handle (wrk, session_handle);
@@ -1879,6 +1988,8 @@ vppcom_session_write_msg (uint32_t session_handle, void *buf, size_t n)
 }
 
 #define vcl_fifo_rx_evt_valid_or_break(_s)                             \
+if (PREDICT_FALSE (!_s->rx_fifo))                                      \
+  break;                                                               \
 if (PREDICT_FALSE (svm_fifo_is_empty (_s->rx_fifo)))                   \
   {                                                                    \
     if (!vcl_session_is_ct (_s))                                       \
@@ -1945,7 +2056,14 @@ vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       break;
     case SESSION_CTRL_EVT_CONNECTED:
       connected_msg = (session_connected_msg_t *) e->data;
-      vcl_session_connected_handler (wrk, connected_msg);
+      sid = vcl_session_connected_handler (wrk, connected_msg);
+      if (sid == VCL_INVALID_SESSION_INDEX)
+       break;
+      if (sid < n_bits && write_map)
+       {
+         clib_bitmap_set_no_check ((uword *) write_map, sid, 1);
+         *bits_set += 1;
+       }
       break;
     case SESSION_CTRL_EVT_DISCONNECTED:
       disconnected_msg = (session_disconnected_msg_t *) e->data;
@@ -2187,75 +2305,75 @@ check_mq:
 }
 
 static inline void
-vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_idx)
+vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_handle)
 {
   vcl_session_t *session;
   vppcom_epoll_t *vep;
-  u32 sid = vep_idx;
+  u32 sh = vep_handle;
 
   if (VPPCOM_DEBUG <= 2)
     return;
 
-  session = vcl_session_get (wrk, vep_idx);
+  session = vcl_session_get_w_handle (wrk, vep_handle);
   if (PREDICT_FALSE (!session))
     {
-      VDBG (0, "ERROR: Invalid vep_idx (%u)!", vep_idx);
+      VDBG (0, "ERROR: Invalid vep_sh (%u)!", vep_handle);
       goto done;
     }
   if (PREDICT_FALSE (!session->is_vep))
     {
-      VDBG (0, "ERROR: vep_idx (%u) is not a vep!", vep_idx);
+      VDBG (0, "ERROR: vep_sh (%u) is not a vep!", vep_handle);
       goto done;
     }
   vep = &session->vep;
-  VDBG (0, "vep_idx (%u): Dumping epoll chain\n"
+  VDBG (0, "vep_sh (%u): Dumping epoll chain\n"
        "{\n"
        "   is_vep         = %u\n"
        "   is_vep_session = %u\n"
-       "   next_sid       = 0x%x (%u)\n"
-       "}\n", vep_idx, session->is_vep, session->is_vep_session,
+       "   next_s       = 0x%x (%u)\n"
+       "}\n", vep_handle, session->is_vep, session->is_vep_session,
        vep->next_sh, vep->next_sh);
 
-  for (sid = vep->next_sh; sid != ~0; sid = vep->next_sh)
+  for (sh = vep->next_sh; sh != ~0; sh = vep->next_sh)
     {
-      session = vcl_session_get (wrk, sid);
+      session = vcl_session_get_w_handle (wrk, sh);
       if (PREDICT_FALSE (!session))
        {
-         VDBG (0, "ERROR: Invalid sid (%u)!", sid);
+         VDBG (0, "ERROR: Invalid sh (%u)!", sh);
          goto done;
        }
       if (PREDICT_FALSE (session->is_vep))
        {
-         VDBG (0, "ERROR: sid (%u) is a vep!", vep_idx);
+         VDBG (0, "ERROR: sh (%u) is a vep!", vep_handle);
        }
       else if (PREDICT_FALSE (!session->is_vep_session))
        {
-         VDBG (0, "ERROR: session (%u) is not a vep session!", sid);
+         VDBG (0, "ERROR: sh (%u) is not a vep session handle!", sh);
          goto done;
        }
       vep = &session->vep;
-      if (PREDICT_FALSE (vep->vep_sh != vep_idx))
-       VDBG (0, "ERROR: session (%u) vep_idx (%u) != vep_idx (%u)!",
-             sid, session->vep.vep_sh, vep_idx);
+      if (PREDICT_FALSE (vep->vep_sh != vep_handle))
+       VDBG (0, "ERROR: session (%u) vep_sh (%u) != vep_sh (%u)!",
+             sh, session->vep.vep_sh, vep_handle);
       if (session->is_vep_session)
        {
-         VDBG (0, "vep_idx[%u]: sid 0x%x (%u)\n"
+         VDBG (0, "vep_sh[%u]: sh 0x%x (%u)\n"
                "{\n"
-               "   next_sid       = 0x%x (%u)\n"
-               "   prev_sid       = 0x%x (%u)\n"
-               "   vep_idx        = 0x%x (%u)\n"
+               "   next_s       = 0x%x (%u)\n"
+               "   prev_s       = 0x%x (%u)\n"
+               "   vep_sh         = 0x%x (%u)\n"
                "   ev.events      = 0x%x\n"
                "   ev.data.u64    = 0x%llx\n"
                "   et_mask        = 0x%x\n"
                "}\n",
-               vep_idx, sid, sid, vep->next_sh, vep->next_sh, vep->prev_sh,
+               vep_handle, sh, sh, vep->next_sh, vep->next_sh, vep->prev_sh,
                vep->prev_sh, vep->vep_sh, vep->vep_sh, vep->ev.events,
                vep->ev.data.u64, vep->et_mask);
        }
     }
 
 done:
-  VDBG (0, "vep_idx (%u): Dump complete!\n", vep_idx);
+  VDBG (0, "vep_sh (%u): Dump complete!\n", vep_handle);
 }
 
 int
@@ -2509,9 +2627,8 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       break;
     case SESSION_CTRL_EVT_CONNECTED:
       connected_msg = (session_connected_msg_t *) e->data;
-      vcl_session_connected_handler (wrk, connected_msg);
+      sid = vcl_session_connected_handler (wrk, connected_msg);
       /* Generate EPOLLOUT because there's no connected event */
-      sid = vcl_session_index_from_vpp_handle (wrk, connected_msg->handle);
       if (!(session = vcl_session_get (wrk, sid)))
        break;
       session_events = session->vep.ev.events;
@@ -2520,6 +2637,8 @@ vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
       add_event = 1;
       events[*num_ev].events |= EPOLLOUT;
       session_evt_data = session->vep.ev.data.u64;
+      if (session->session_state & STATE_FAILED)
+       events[*num_ev].events |= EPOLLHUP;
       break;
     case SESSION_CTRL_EVT_DISCONNECTED:
       disconnected_msg = (session_disconnected_msg_t *) e->data;
@@ -2829,6 +2948,24 @@ vppcom_session_attr (uint32_t session_handle, uint32_t op,
        rv = VPPCOM_EINVAL;
       break;
 
+    case VPPCOM_ATTR_SET_LCL_ADDR:
+      if (PREDICT_TRUE (buffer && buflen &&
+                       (*buflen >= sizeof (*ep)) && ep->ip))
+       {
+         session->transport.is_ip4 = ep->is_ip4;
+         session->transport.lcl_port = ep->port;
+         vcl_ip_copy_from_ep (&session->transport.lcl_ip, ep);
+         *buflen = sizeof (*ep);
+         VDBG (1, "VPPCOM_ATTR_SET_LCL_ADDR: sh %u, is_ip4 = %u, addr = %U"
+               " port %d", session_handle, ep->is_ip4, format_ip46_address,
+               &session->transport.lcl_ip,
+               ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
+               clib_net_to_host_u16 (ep->port));
+       }
+      else
+       rv = VPPCOM_EINVAL;
+      break;
+
     case VPPCOM_ATTR_GET_LIBC_EPFD:
       rv = session->libc_epfd;
       VDBG (2, "VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d", rv);