+ clib_error_t *err;
+ int n_tries = 5;
+
+ while (1)
+ {
+ err = clib_socket_recvmsg (cs, msg, msg_len, 0, 0);
+ if (!err)
+ break;
+
+ if (!n_tries)
+ return err;
+
+ n_tries--;
+ usleep (1);
+ }
+
+ return err;
+}
+
+static void
+sapi_add_del_cert_key_handler (app_namespace_t *app_ns, clib_socket_t *cs,
+ app_sapi_cert_key_add_del_msg_t *mp)
+{
+ vnet_app_add_cert_key_pair_args_t _a, *a = &_a;
+ app_sapi_cert_key_add_del_reply_msg_t *rmp;
+ app_sapi_msg_t msg = { 0 };
+ int rv = 0;
+
+ if (mp->is_add)
+ {
+ const u32 max_certkey_len = 2e4, max_cert_len = 1e4, max_key_len = 1e4;
+ clib_error_t *err;
+ u8 *certkey = 0;
+ u32 key_len;
+
+ if (mp->certkey_len > max_certkey_len)
+ {
+ rv = SESSION_E_INVALID;
+ goto send_reply;
+ }
+
+ vec_validate (certkey, mp->certkey_len - 1);
+
+ err = sapi_socket_receive_wait (cs, certkey, mp->certkey_len);
+ if (err)
+ {
+ clib_error_report (err);
+ rv = SESSION_E_INVALID;
+ goto send_reply;
+ }
+
+ if (mp->cert_len > max_cert_len)
+ {
+ rv = SESSION_E_INVALID;
+ goto send_reply;
+ }
+
+ if (mp->certkey_len < mp->cert_len)
+ {
+ rv = SESSION_E_INVALID;
+ goto send_reply;
+ }
+
+ key_len = mp->certkey_len - mp->cert_len;
+ if (key_len > max_key_len)
+ {
+ rv = SESSION_E_INVALID;
+ goto send_reply;
+ }
+
+ clib_memset (a, 0, sizeof (*a));
+ a->cert = certkey;
+ a->key = certkey + mp->cert_len;
+ a->cert_len = mp->cert_len;
+ a->key_len = key_len;
+ rv = vnet_app_add_cert_key_pair (a);
+
+ vec_free (certkey);
+ }
+ else
+ {
+ rv = vnet_app_del_cert_key_pair (mp->index);
+ }
+
+send_reply:
+
+ msg.type = APP_SAPI_MSG_TYPE_ADD_DEL_CERT_KEY_REPLY;
+ rmp = &msg.cert_key_add_del_reply;
+ rmp->retval = rv;
+ rmp->context = mp->context;
+ if (!rv && mp->is_add)
+ rmp->index = a->index;
+
+ clib_socket_sendmsg (cs, &msg, sizeof (msg), 0, 0);
+}
+
+static void
+sapi_socket_detach (app_namespace_t * app_ns, clib_socket_t * cs)
+{
+ app_ns_api_handle_t *handle;
+ app_worker_t *app_wrk;
+ u32 api_client_handle;
+
+ api_client_handle = appns_sapi_socket_handle (app_ns, cs);
+
+ /* Cleanup everything because app worker closed socket or crashed */
+ handle = (app_ns_api_handle_t *) & cs->private_data;
+ app_wrk = app_worker_get (handle->aah_app_wrk_index);
+
+ vnet_app_worker_add_del_args_t args = {
+ .app_index = app_wrk->app_index,
+ .wrk_map_index = app_wrk->wrk_map_index,
+ .api_client_index = api_client_handle,
+ .is_add = 0
+ };
+ /* Send rpc to main thread for worker barrier */
+ vlib_rpc_call_main_thread (vnet_app_worker_add_del, (u8 *) & args,
+ sizeof (args));
+}
+
+static clib_error_t *
+sapi_sock_read_ready (clib_file_t * cf)
+{
+ app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
+ vlib_main_t *vm = vlib_get_main ();
+ app_sapi_msg_t msg = { 0 };
+ app_namespace_t *app_ns;
+ clib_error_t *err = 0;
+ clib_socket_t *cs;
+
+ app_ns = app_namespace_get (handle->aah_app_ns_index);
+ cs = appns_sapi_get_socket (app_ns, handle->aah_sock_index);
+ if (!cs)
+ goto error;
+
+ err = clib_socket_recvmsg (cs, &msg, sizeof (msg), 0, 0);
+ if (err)
+ {
+ clib_error_free (err);
+ sapi_socket_detach (app_ns, cs);
+ goto error;
+ }
+
+ handle = (app_ns_api_handle_t *) & cs->private_data;
+
+ vlib_worker_thread_barrier_sync (vm);
+
+ switch (msg.type)
+ {
+ case APP_SAPI_MSG_TYPE_ATTACH:
+ session_api_attach_handler (app_ns, cs, &msg.attach);
+ break;
+ case APP_SAPI_MSG_TYPE_ADD_DEL_WORKER:
+ sapi_add_del_worker_handler (app_ns, cs, &msg.worker_add_del);
+ break;
+ case APP_SAPI_MSG_TYPE_ADD_DEL_CERT_KEY:
+ sapi_add_del_cert_key_handler (app_ns, cs, &msg.cert_key_add_del);
+ break;
+ default:
+ clib_warning ("app wrk %u unknown message type: %u",
+ handle->aah_app_wrk_index, msg.type);
+ break;
+ }
+
+ vlib_worker_thread_barrier_release (vm);
+
+error:
+ return 0;
+}
+
+static clib_error_t *
+sapi_sock_write_ready (clib_file_t * cf)
+{
+ app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
+ clib_warning ("called for app ns %u", handle->aah_app_ns_index);
+ return 0;
+}
+
+static clib_error_t *
+sapi_sock_error (clib_file_t * cf)
+{
+ app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
+ app_namespace_t *app_ns;
+ clib_socket_t *cs;
+
+ app_ns = app_namespace_get (handle->aah_app_ns_index);
+ cs = appns_sapi_get_socket (app_ns, handle->aah_sock_index);
+ if (!cs)
+ return 0;
+
+ sapi_socket_detach (app_ns, cs);
+ return 0;
+}
+
+static clib_error_t *
+sapi_sock_accept_ready (clib_file_t * scf)
+{
+ app_ns_api_handle_t handle = *(app_ns_api_handle_t *) & scf->private_data;
+ app_namespace_t *app_ns;
+ clib_file_t cf = { 0 };
+ clib_error_t *err = 0;
+ clib_socket_t *ccs, *scs;
+
+ /* Listener files point to namespace */
+ app_ns = app_namespace_get (handle.aah_app_ns_index);
+
+ /*
+ * Initialize client socket
+ */
+ ccs = appns_sapi_alloc_socket (app_ns);
+
+ /* Grab server socket after client is initialized */
+ scs = appns_sapi_get_socket (app_ns, handle.aah_sock_index);
+ if (!scs)
+ goto error;
+
+ err = clib_socket_accept (scs, ccs);
+ if (err)
+ {
+ clib_error_report (err);
+ goto error;
+ }
+
+ cf.read_function = sapi_sock_read_ready;
+ cf.write_function = sapi_sock_write_ready;
+ cf.error_function = sapi_sock_error;
+ cf.file_descriptor = ccs->fd;
+ /* File points to app namespace and socket */
+ handle.aah_sock_index = appns_sapi_socket_index (app_ns, ccs);
+ cf.private_data = handle.as_u64;
+ cf.description = format (0, "app sock conn fd: %d", ccs->fd);
+
+ /* Poll until we get an attach message. Socket points to file and
+ * application that owns the socket */
+ handle.aah_app_wrk_index = APP_INVALID_INDEX;
+ handle.aah_file_index = clib_file_add (&file_main, &cf);
+ ccs->private_data = handle.as_u64;
+
+ return err;
+
+error:
+ appns_sapi_free_socket (app_ns, ccs);
+ return err;
+}
+
+void
+appns_sapi_del_ns_socket (app_namespace_t *app_ns)
+{
+ app_ns_api_handle_t *handle;
+ clib_socket_t *cs;
+
+ pool_foreach (cs, app_ns->app_sockets)
+ {
+ handle = (app_ns_api_handle_t *) &cs->private_data;
+ clib_file_del_by_index (&file_main, handle->aah_file_index);
+
+ clib_socket_close (cs);
+ clib_socket_free (cs);
+ }
+ pool_free (app_ns->app_sockets);
+}
+
+int
+appns_sapi_add_ns_socket (app_namespace_t * app_ns)
+{
+ char *subdir = "/app_ns_sockets/";
+ app_ns_api_handle_t *handle;
+ clib_file_t cf = { 0 };
+ struct stat file_stat;
+ clib_error_t *err;
+ clib_socket_t *cs;
+ char dir[4096];
+
+ if (app_ns->netns)
+ {
+ if (!app_ns->sock_name)
+ app_ns->sock_name = format (0, "@vpp/session/%v%c", app_ns->ns_id, 0);
+ if (app_ns->sock_name[0] != '@')
+ return VNET_API_ERROR_INVALID_VALUE;
+ }
+ else
+ {
+ snprintf (dir, sizeof (dir), "%s%s", vlib_unix_get_runtime_dir (),
+ subdir);
+ err = vlib_unix_recursive_mkdir ((char *) dir);
+ if (err)
+ {
+ clib_error_report (err);
+ return VNET_API_ERROR_SYSCALL_ERROR_1;
+ }
+
+ if (!app_ns->sock_name)
+ app_ns->sock_name = format (0, "%s%v%c", dir, app_ns->ns_id, 0);
+ }
+
+ /*
+ * Create and initialize socket to listen on
+ */
+ cs = appns_sapi_alloc_socket (app_ns);
+ cs->config = (char *) vec_dup (app_ns->sock_name);
+ cs->flags = CLIB_SOCKET_F_IS_SERVER |
+ CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
+ CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+
+ if ((err = clib_socket_init_netns (cs, app_ns->netns)))
+ {
+ clib_error_report (err);
+ return -1;
+ }
+
+ if (!app_ns->netns && stat ((char *) app_ns->sock_name, &file_stat) == -1)
+ return -1;