aboutsummaryrefslogtreecommitdiffstats
path: root/src/srvpp
diff options
context:
space:
mode:
authorHongjun Ni <hongjun.ni@intel.com>2018-11-20 03:35:38 +0800
committerHongjun Ni <hongjun.ni@intel.com>2018-11-20 03:41:08 +0800
commitfd6c6f7c42d148a980030c7535846a4643d8cc9b (patch)
tree42df5fa7198777b4f3f711c0142f6cb0a7e7fea5 /src/srvpp
parenta45498c3a487d9fb561c24b2f3df7910fa54c0f8 (diff)
Initial code commit for Sweetcomb project
The initial code is contruted by Cisco and HuachenTel. It supports three key features: 1). Keeps compatible with IETF Yang Model and Honeycomb Yang Model. 2). Supports mulitiple clients to configure VPP simultaneously. 3). Automatically connect to VPP. Change-Id: I3dda4fddb14062e5f113d505c6a7ee64d60c44c0 Signed-off-by: Rastislav Szabo <raszabo@cisco.com> Signed-off-by: Chuanguo Wang <wangchuanguo@huachentel.com> Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
Diffstat (limited to 'src/srvpp')
-rw-r--r--src/srvpp/CMakeLists.txt40
-rw-r--r--src/srvpp/libsrvpp.pc.in10
-rw-r--r--src/srvpp/src/CMakeLists.txt44
-rw-r--r--src/srvpp/src/srvpp.c823
-rw-r--r--src/srvpp/src/srvpp.h185
-rw-r--r--src/srvpp/src/srvpp_logger.c83
-rw-r--r--src/srvpp/src/srvpp_logger.h89
-rw-r--r--src/srvpp/tests/CMakeLists.txt40
-rw-r--r--src/srvpp/tests/srvpp_test.c160
9 files changed, 1474 insertions, 0 deletions
diff --git a/src/srvpp/CMakeLists.txt b/src/srvpp/CMakeLists.txt
new file mode 100644
index 0000000..20fe6f0
--- /dev/null
+++ b/src/srvpp/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2016 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.
+#
+
+cmake_minimum_required(VERSION 2.8)
+project(srvpp)
+
+# add subdirectories
+add_subdirectory(src)
+
+# enable testing if requested and possible
+SET(ENABLE_TESTS 1 CACHE BOOL "Enable unit tests.")
+if(ENABLE_TESTS)
+ find_package(CMOCKA)
+ if(CMOCKA_FOUND)
+ MESSAGE(STATUS "CMocka found, tests are enabled.")
+ enable_testing()
+ add_subdirectory(tests)
+ else(CMOCKA_FOUND)
+ MESSAGE(WARNING "CMocka not found, tests are disabled.")
+ endif(CMOCKA_FOUND)
+endif(ENABLE_TESTS)
+
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+ # generate and install pkg-config file
+ configure_file("libsrvpp.pc.in" "libsrvpp.pc" @ONLY)
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libsrvpp.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+endif()
diff --git a/src/srvpp/libsrvpp.pc.in b/src/srvpp/libsrvpp.pc.in
new file mode 100644
index 0000000..c57c267
--- /dev/null
+++ b/src/srvpp/libsrvpp.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+
+Name: srvpp
+Description: VPP-Sysrepo integration library
+Version: 0.0.1
+Libs: -L${libdir} -lsrvpp
+Cflags: -I${includedir}
+
diff --git a/src/srvpp/src/CMakeLists.txt b/src/srvpp/src/CMakeLists.txt
new file mode 100644
index 0000000..7fec80d
--- /dev/null
+++ b/src/srvpp/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2016 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(GNUInstallDirs)
+
+# srvpp sources
+set(SRVPP_SOURCES
+ srvpp_logger.c
+ srvpp.c
+)
+
+# srvpp public headers
+set(SRVPP_HEADERS
+ srvpp.h
+)
+
+set(CMAKE_C_FLAGS " -g -O0 -fpic -fPIC -std=gnu99 -Wl,-rpath-link=/usr/lib")
+
+# libraries to link with
+set(LINK_LIBRARIES sysrepo vlibmemoryclient vapiclient vppapiclient svm vppinfra pthread rt dl)
+
+# build instructions
+add_library(srvpp SHARED ${SRVPP_SOURCES})
+add_library(srvpp_a ${SRVPP_SOURCES})
+
+# linker instructions
+target_link_libraries(srvpp ${LINK_LIBRARIES})
+target_link_libraries(srvpp_a ${LINK_LIBRARIES})
+
+# install rules
+install(TARGETS srvpp DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(FILES ${SRVPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/src/srvpp/src/srvpp.c b/src/srvpp/src/srvpp.c
new file mode 100644
index 0000000..9b3467b
--- /dev/null
+++ b/src/srvpp/src/srvpp.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (c) 2016 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 <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include "srvpp.h"
+#include "srvpp_logger.h"
+
+#undef vl_api_version
+#define vl_api_version(n,v) static u32 vpe_api_version = v;
+#include <vpp/api/vpe.api.h>
+#undef vl_api_version
+
+
+#define SRVPP_RESPONSE_TIMEOUT 2 /**< Maximum time (in seconds) that a client waits for the response(s) to a request or dumprequest. */
+
+#define CHECK_NULL(ARG) \
+ if (NULL == ARG) { \
+ SRVPP_LOG_ERR("NULL value detected for %s argument of %s", #ARG, __func__); \
+ return -1; \
+ } \
+
+#define CHECK_NULL_RET(ARG, RET) \
+ if (NULL == ARG) { \
+ SRVPP_LOG_ERR("NULL value detected for %s argument of %s", #ARG, __func__); \
+ return RET; \
+ } \
+
+/**
+ * @brief Type of the response expected from VPP.
+ */
+typedef enum srvpp_response_type_e {
+ SRVPP_REPLY, /**< A reply message (single message). */
+ SRVPP_DETAILS, /**< Multiple details messages. */
+} srvpp_response_type_t;
+
+/**
+ * @brief srvpp request context structure.
+ */
+typedef struct srvpp_request_ctx_s {
+ struct srvpp_request_ctx_s *_next; /**< Pointer to the next request context in the linked-list. */
+ u32 ctx_id; /**< Context ID used to map responses with requests. */
+ srvpp_response_type_t resp_type; /**< Type of the response expected from VPP. */
+
+ i32 resp_retval; /**< Return value of the last response (0 = success). */
+ bool resp_data_copy; /**< Controls whether data of the responses shall be copied (returned) or ignored. */
+ void **resp_msg_arr; /**< Array of the pointers to response messages. */
+ size_t *resp_msg_sizes; /**< Array of sizes of messages in the ::resp_msg_arr. */
+ size_t resp_msg_arr_size; /**< Size of the ::resp_msg_arr array. */
+ size_t resp_msg_cnt; /**< Count of the messages currently stored in ::resp_msg_arr array. */
+
+ bool resp_ready; /**< Signals that the expected response has arrived and is ready to be returned. */
+ pthread_cond_t resp_cv; /**< Condition variable for ::resp_ready. */
+ pthread_mutex_t lock; /**< Mutex to protect shared access to the context. */
+} srvpp_request_ctx_t;
+
+/**
+ * @brief srvpp interface info context structure.
+ */
+typedef struct srvpp_if_info_s {
+ struct srvpp_if_info_s *_next;
+ const char *if_name;
+ u32 if_index;
+} srvpp_if_info_t;
+
+/**
+ * @brief srvpp context structure.
+ */
+typedef struct srvpp_ctx_s {
+ size_t ref_cnt; /**< Context reference counter. */
+ unix_shared_memory_queue_t *vlib_input_queue; /**< VPP Library input queue. */
+ u32 vlib_client_index; /**< VPP Library client index. */
+ srvpp_request_ctx_t *request_ctx_list; /**< Linked-list of request contexts. */
+ srvpp_if_info_t *if_info_list; /**< Linked-list of VPP interfaces information. */
+ pthread_key_t request_key; /**< Key to the thread-specific request context. */
+} srvpp_ctx_t;
+
+/**
+ * @brief Generic VPP request structure.
+ */
+typedef struct __attribute__ ((packed)) vl_generic_request_s {
+ unsigned short _vl_msg_id;
+ unsigned int client_index;
+ unsigned int context;
+} vl_generic_request_t;
+
+/**
+ * @brief Generic VPP response structure.
+ */
+typedef struct __attribute__ ((packed)) vl_generic_response_s {
+ u16 _vl_msg_id;
+ u32 context;
+} vl_generic_response_t;
+
+/**
+ * @brief Generic VPP reply structure (response with a single message).
+ */
+typedef struct __attribute__ ((packed)) vl_generic_reply_s {
+ u16 _vl_msg_id;
+ u32 context;
+ i32 retval;
+} vl_generic_reply_t;
+
+/**
+ * @brief Global srvpp context.
+ */
+static srvpp_ctx_t *srvpp_ctx = NULL;
+
+/**
+ * @brief Mutex for the global context.
+ */
+static pthread_mutex_t srvpp_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * @brief Not used, just to satisfy external references when -lvlib is not available.
+ */
+void
+vlib_cli_output(struct vlib_main_t *vm, char *fmt, ...)
+{
+ SRVPP_LOG_WRN_MSG("vlib_cli_output callled!");
+}
+
+/**
+ * @brief Sets correct VPP API version.
+ */
+void
+vl_client_add_api_signatures(vl_api_memclnt_create_t *mp)
+{
+ /*
+ * Send the main API signature in slot 0. This bit of code must
+ * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
+ */
+ mp->api_versions[0] = clib_host_to_net_u32(vpe_api_version);
+}
+
+/**
+ * @brief Returns the request context assigned to the active thread.
+ * If no request context exists for the thread, it will be automatically created.
+ */
+static srvpp_request_ctx_t *
+srvpp_get_thread_request_ctx(srvpp_ctx_t *ctx)
+{
+ srvpp_request_ctx_t *req_ctx = NULL;
+
+ CHECK_NULL_RET(ctx, NULL);
+
+ if (NULL == (req_ctx = pthread_getspecific(ctx->request_key))) {
+ /* allocate a new request context */
+ req_ctx = calloc(1, sizeof(*req_ctx));
+ if (NULL != req_ctx) {
+ /* initialize the new context */
+ req_ctx->ctx_id = (u32)(((uintptr_t)req_ctx) & 0xFFFFFFFF) << 16;
+ SRVPP_LOG_DBG("Creating new request ctx with id=%u", req_ctx->ctx_id);
+
+ pthread_mutex_init(&req_ctx->lock, NULL);
+ pthread_cond_init (&req_ctx->resp_cv, NULL);
+
+ /* save the request ctx in the srvpp context */
+ pthread_mutex_lock(&srvpp_ctx_lock);
+ req_ctx->_next = ctx->request_ctx_list;
+ ctx->request_ctx_list = req_ctx;
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ /* save the request ctx in the thread-local memory */
+ pthread_setspecific(ctx->request_key, req_ctx);
+ } else {
+ SRVPP_LOG_ERR_MSG("Unable to allocate new request context.");
+ }
+ }
+
+ return req_ctx;
+}
+
+/**
+ * @brief Returns the request context matching with the provided context id.
+ */
+static srvpp_request_ctx_t *
+srvpp_get_request_ctx(srvpp_ctx_t *ctx, u32 req_ctx_id)
+{
+ srvpp_request_ctx_t *req_ctx = NULL, *match = NULL;
+
+ CHECK_NULL_RET(ctx, NULL);
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ req_ctx = ctx->request_ctx_list;
+
+ while (NULL != req_ctx) {
+ if (req_ctx->ctx_id == req_ctx_id) {
+ match = req_ctx;
+ break;
+ }
+ req_ctx = req_ctx->_next;
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ return match;
+}
+
+/**
+ * @brief Copy data of the message into the provided request context.
+ */
+static int
+srvpp_msg_data_copy(srvpp_request_ctx_t *req_ctx, void *msg, size_t msg_size)
+{
+ void **msg_arr = NULL;
+ size_t *sizes_arr = NULL;
+ void *msg_space = NULL;
+
+ CHECK_NULL(req_ctx);
+
+ if (req_ctx->resp_msg_arr_size < (req_ctx->resp_msg_cnt + 1)) {
+ /* reallocate arrays to fit one new message */
+ msg_arr = realloc(req_ctx->resp_msg_arr, (req_ctx->resp_msg_cnt + 1) * sizeof(*req_ctx->resp_msg_arr));
+ if (NULL == msg_arr) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message array.");
+ return -1;
+ }
+ sizes_arr = realloc(req_ctx->resp_msg_sizes, (req_ctx->resp_msg_cnt + 1) * sizeof(*req_ctx->resp_msg_sizes));
+ if (NULL == sizes_arr) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message sizes array.");
+ return -1;
+ }
+ req_ctx->resp_msg_arr = msg_arr;
+ req_ctx->resp_msg_sizes = sizes_arr;
+ req_ctx->resp_msg_arr_size = req_ctx->resp_msg_cnt + 1;
+
+ req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt] = NULL;
+ req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] = 0;
+ }
+
+ if (req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] < msg_size) {
+ /* reallocate space for the message */
+ msg_space = realloc(req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt], msg_size);
+ if (NULL == msg_space) {
+ SRVPP_LOG_ERR_MSG("Unable to reallocate message space.");
+ return -1;
+ }
+ req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt] = msg_space;
+ req_ctx->resp_msg_sizes[req_ctx->resp_msg_cnt] = msg_size;
+ }
+
+ /* copy the message content */
+ memcpy(req_ctx->resp_msg_arr[req_ctx->resp_msg_cnt], msg, msg_size);
+
+ req_ctx->resp_msg_cnt++;
+
+ return 0;
+}
+
+/**
+ * @brief Processes a reply to a single VPP request.
+ */
+static int
+srvpp_process_reply_msg(srvpp_request_ctx_t *req_ctx, u16 msg_id, void *msg, size_t msg_size)
+{
+ vl_generic_reply_t *reply = NULL;
+ int rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ reply = (vl_generic_reply_t *) msg;
+
+ if (req_ctx->ctx_id != reply->context) {
+ SRVPP_LOG_ERR_MSG("Invalid request context for provided message, ignoring the message.");
+ return -1;
+ }
+
+ if (req_ctx->resp_data_copy) {
+ /* copy msg data into req_ctx */
+ rc = srvpp_msg_data_copy(req_ctx, msg, msg_size);
+ }
+
+ if (0 == rc) {
+ req_ctx->resp_retval = reply->retval;
+ } else {
+ req_ctx->resp_retval = rc;
+ }
+
+ /* signal the requesting thread */
+ req_ctx->resp_ready = true;
+ pthread_cond_signal(&req_ctx->resp_cv);
+
+ return rc;
+}
+
+/**
+ * @brief Processes a reply to a dump request to VPP (response consisting of multiple messages).
+ */
+static int
+srvpp_process_details_msg(srvpp_request_ctx_t *req_ctx, u16 msg_id, void *msg, size_t msg_size)
+{
+ vl_generic_response_t *response = NULL;
+ int rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ response = (vl_generic_response_t *) msg;
+
+ if (req_ctx->ctx_id != response->context) {
+ SRVPP_LOG_ERR_MSG("Invalid request context for provided message, ignoring the message.");
+ return -1;
+ }
+
+ if (VL_API_CONTROL_PING_REPLY != msg_id) {
+ /* details message - copy message data into req contex*/
+ rc = srvpp_msg_data_copy(req_ctx, msg, msg_size);
+ if (0 != rc && 0 == req_ctx->resp_retval) {
+ /* in case of error, propagate it to the req context */
+ req_ctx->resp_retval = rc;
+ }
+ } else {
+ /* control ping reply, signal the requesting thread */
+ req_ctx->resp_ready = true;
+ pthread_cond_signal(&req_ctx->resp_cv);
+ }
+
+ return req_ctx->resp_retval;
+}
+
+/**
+ * @brief Internal callback automatically called by VPP library when a message
+ * from VPP is received.
+ */
+void
+_srvpp_receive_msg_handler(void *msg)
+{
+ srvpp_request_ctx_t *req_ctx = NULL;
+ vl_generic_response_t *response = NULL;
+ msgbuf_t *msg_header = NULL;
+ size_t msg_size = 0;
+ u16 msg_id = 0;
+
+ if (NULL == msg) {
+ SRVPP_LOG_WRN_MSG("NULL message received, ignoring.");
+ return;
+ }
+
+ response = (vl_generic_response_t *) msg;
+
+ /* get message details */
+ msg_header = (msgbuf_t *) (((u8 *) msg) - offsetof(msgbuf_t, data));
+ msg_size = ntohl(msg_header->data_len);
+ msg_id = ntohs(*((u16 *)msg));
+
+ SRVPP_LOG_DBG("New message received from VPP (id=%d, size=%zu).", msg_id, msg_size);
+
+ /* get request context matching with context id */
+ req_ctx = srvpp_get_request_ctx(srvpp_ctx, response->context);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_WRN("Unexpected context id=%d within the received message, ignoring.", response->context);
+ return;
+ }
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ if (SRVPP_REPLY == req_ctx->resp_type) {
+ srvpp_process_reply_msg(req_ctx, msg_id, msg, msg_size);
+ } else {
+ srvpp_process_details_msg(req_ctx, msg_id, msg, msg_size);
+ }
+
+ pthread_mutex_unlock(&req_ctx->lock);
+}
+
+/**
+ * @brief Blocks the thread until a response from VPP comes or until a timeout expires.
+ */
+static int
+srvpp_wait_response(srvpp_request_ctx_t *req_ctx)
+{
+ struct timespec ts = { 0, };
+ int retval = 0, rc = 0;
+
+ CHECK_NULL(req_ctx);
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += SRVPP_RESPONSE_TIMEOUT;
+
+ while (!req_ctx->resp_ready && (0 == rc)) {
+ rc = pthread_cond_timedwait(&req_ctx->resp_cv, &req_ctx->lock, &ts);
+ }
+ if (0 == rc) {
+ SRVPP_LOG_DBG("Received the response from VPP, retval=%d", req_ctx->resp_retval);
+ retval = req_ctx->resp_retval;
+ } else {
+ SRVPP_LOG_ERR("Response not received from VPP within the timeout period (%d sec).", SRVPP_RESPONSE_TIMEOUT);
+ retval = -1;
+ }
+
+ /* invalidate previous context id */
+ ++req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ return retval;
+}
+
+/**
+ * @brief Connects to VPP.
+ */
+static int
+srvpp_vlib_connect(srvpp_ctx_t *ctx)
+{
+ api_main_t *am = &api_main;
+ int rc = 0;
+
+ CHECK_NULL(ctx);
+
+ SRVPP_LOG_DBG_MSG("Connecting to VPP...");
+
+ rc = vl_client_connect_to_vlib("/vpe-api", "srvpp", 32);
+
+ if (rc < 0) {
+ SRVPP_LOG_ERR("Unable to connect to VPP, rc=%d.", rc);
+ } else {
+ SRVPP_LOG_DBG("Connection to VPP established, client index=%d.", am->my_client_index);
+ ctx->vlib_client_index = am->my_client_index;
+ ctx->vlib_input_queue = am->shmem_hdr->vl_input_queue;
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Disconnects from VPP.
+ */
+static void
+srvpp_vlib_disconnect()
+{
+ vl_client_disconnect_from_vlib();
+}
+
+/**
+ * @brief Adds a new interface into interfaces list.
+ */
+static int
+srvpp_if_info_add(srvpp_ctx_t *ctx, const char *if_name, u32 if_index)
+{
+ srvpp_if_info_t *tmp = NULL, *if_info = NULL;
+
+ SRVPP_LOG_DBG("Adding interface '%s', id=%d", if_name, if_index);
+
+ if_info = calloc(1, sizeof(*if_info));
+ if (NULL == if_info) {
+ return 1;
+ }
+
+ if_info->if_name = strdup(if_name);
+ if (NULL == if_info->if_name) {
+ return 1;
+ }
+
+ if_info->if_index = if_index;
+
+ if (NULL == ctx->if_info_list) {
+ ctx->if_info_list = if_info;
+ } else {
+ tmp = ctx->if_info_list;
+ while (NULL != tmp->_next) {
+ tmp = tmp->_next;
+ }
+ tmp->_next = if_info;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Loads VPP interfaces information.
+ */
+static int
+srvpp_if_info_load(srvpp_ctx_t *ctx)
+{
+ vl_api_sw_interface_dump_t *if_dump_req = NULL;
+ vl_api_sw_interface_details_t *if_details = NULL;
+ void **details = NULL;
+ size_t details_cnt = 0;
+ int ret = 0;
+
+ SRVPP_LOG_DBG_MSG("Loading VPP interfaces information");
+
+ /* dump interfaces */
+ if_dump_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_DUMP, sizeof(*if_dump_req));
+
+ ret = srvpp_send_dumprequest(ctx, if_dump_req, &details, &details_cnt);
+ if (0 != ret) {
+ return ret;
+ }
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ for (size_t i = 0; i < details_cnt; i++) {
+ if_details = (vl_api_sw_interface_details_t *) details[i];
+ ret = srvpp_if_info_add(ctx, (char*)if_details->interface_name, ntohl(if_details->sw_if_index));
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ return 0;
+}
+
+/**
+ * @brief Cleans up VPP interfaces information.
+ */
+static void
+srvpp_if_info_cleanup(srvpp_ctx_t *ctx)
+{
+ srvpp_if_info_t *tmp = NULL, *if_info = NULL;
+
+ if (NULL != ctx) {
+ if_info = ctx->if_info_list;
+
+ while (NULL != if_info) {
+ tmp = if_info;
+ if_info = if_info->_next;
+ free((void*)tmp->if_name);
+ free(tmp);
+ }
+ }
+}
+
+/**
+ * @brief Initializes the srvpp context.
+ */
+srvpp_ctx_t *
+srvpp_ctx_init()
+{
+ srvpp_ctx_t *ctx = NULL;
+ int rc = 0;
+
+ srvpp_logger_init();
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (NULL == ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to allocate srvpp context.");
+ return NULL;
+ }
+
+ rc = srvpp_vlib_connect(ctx);
+ if (0 != rc) {
+ SRVPP_LOG_ERR_MSG("Unable to initialize srvpp context.");
+ free(ctx);
+ return NULL;
+ }
+
+ while (pthread_key_create(&ctx->request_key, NULL) == EAGAIN);
+ pthread_setspecific(ctx->request_key, NULL);
+
+ ctx->ref_cnt = 1;
+
+ SRVPP_LOG_INF_MSG("srvpp context initialized successfully.");
+
+ return ctx;
+}
+
+/**
+ * @brief Cleans up the srvpp context.
+ */
+static void
+srvpp_ctx_cleanup(srvpp_ctx_t *ctx)
+{
+ srvpp_request_ctx_t *tmp = NULL, *req_ctx = NULL;
+
+ if (NULL != ctx) {
+ srvpp_vlib_disconnect();
+
+ srvpp_if_info_cleanup(ctx);
+
+ req_ctx = ctx->request_ctx_list;
+ while (NULL != req_ctx) {
+ tmp = req_ctx;
+ req_ctx = req_ctx->_next;
+
+ pthread_mutex_destroy(&tmp->lock);
+ pthread_cond_destroy(&tmp->resp_cv);
+
+ for (size_t i = 0; i < tmp->resp_msg_arr_size; i++) {
+ free(tmp->resp_msg_arr[i]);
+ }
+ free(tmp->resp_msg_arr);
+ free(tmp->resp_msg_sizes);
+
+ free(tmp);
+ }
+ free(ctx);
+
+ srvpp_logger_cleanup();
+
+ SRVPP_LOG_INF_MSG("srvpp context cleaned up successfully.");
+ }
+}
+
+srvpp_ctx_t *
+srvpp_get_ctx()
+{
+ bool setup_handlers = false;
+
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ if (NULL == srvpp_ctx) {
+ /* initialize a new context */
+ SRVPP_LOG_DBG_MSG("Initializing a new srvpp context.");
+ srvpp_ctx = srvpp_ctx_init();
+ setup_handlers = true;
+ } else {
+ /* increment ref. count */
+ srvpp_ctx->ref_cnt++;
+ SRVPP_LOG_DBG("Reusing existing srvpp context, new refcount=%zu.", srvpp_ctx->ref_cnt);
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+
+ if (setup_handlers) {
+ /* setup required handlers */
+ srvpp_setup_handler(CONTROL_PING_REPLY, control_ping_reply);
+ srvpp_setup_handler(SW_INTERFACE_DETAILS, sw_interface_details);
+
+ /* load VPP interfaces information */
+ srvpp_if_info_load(srvpp_ctx);
+ }
+
+ return srvpp_ctx;
+}
+
+void
+srvpp_release_ctx(srvpp_ctx_t *ctx)
+{
+ pthread_mutex_lock(&srvpp_ctx_lock);
+
+ if (ctx != srvpp_ctx) {
+ SRVPP_LOG_ERR_MSG("Invalid srvpp context passed in, unable to release.");
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+ return;
+ }
+
+ if (srvpp_ctx->ref_cnt > 1) {
+ /* there are still some other references */
+ srvpp_ctx->ref_cnt--;
+ SRVPP_LOG_DBG("Releasing a reference to srvpp context, new refcount=%zu.", srvpp_ctx->ref_cnt);
+ } else {
+ /* last reference - cleanup */
+ SRVPP_LOG_DBG_MSG("Releasing srvpp context (last ctx reference).");
+ srvpp_ctx_cleanup(srvpp_ctx);
+ srvpp_ctx = NULL;
+ }
+
+ pthread_mutex_unlock(&srvpp_ctx_lock);
+}
+
+void *
+srvpp_alloc_msg(uint16_t msg_id, size_t msg_size)
+{
+ vl_generic_request_t *req = NULL;
+
+ req = vl_msg_api_alloc(msg_size);
+ if (NULL != req) {
+ memset(req, 0, msg_size);
+ req->_vl_msg_id = ntohs(msg_id);
+ }
+
+ return req;
+}
+
+int
+srvpp_send_request(srvpp_ctx_t *ctx, void *request, void **response)
+{
+ vl_generic_request_t *req = NULL;
+ srvpp_request_ctx_t *req_ctx = NULL;
+ int retval = 0;
+
+ CHECK_NULL(ctx);
+ CHECK_NULL(request);
+
+ req_ctx = srvpp_get_thread_request_ctx(ctx);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to obtain a request context.");
+ return -1;
+ }
+
+ req = (vl_generic_request_t *) request;
+ req->client_index = ctx->vlib_client_index;
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ if (NULL != response) {
+ /* response data is requested */
+ req_ctx->resp_data_copy = true;
+ req_ctx->resp_msg_cnt = 0;
+ } else {
+ /* not interested in response data */
+ req_ctx->resp_data_copy = false;
+ }
+
+ req_ctx->resp_type = SRVPP_REPLY;
+ req_ctx->resp_ready = false;
+ req_ctx->resp_retval = 0;
+ req->context = ++req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ SRVPP_LOG_DBG_MSG("Sending a request to VPP.");
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&req);
+
+ /* wait for expected response */
+ retval = srvpp_wait_response(req_ctx);
+
+ if (0 == retval) {
+ if (NULL != response && req_ctx->resp_msg_cnt > 0) {
+ *response = req_ctx->resp_msg_arr[0];
+ }
+ SRVPP_LOG_DBG_MSG("VPP request successfully processed.");
+ } else {
+ SRVPP_LOG_ERR_MSG("Error by handling of a VPP request.");
+ }
+
+ return retval;
+}
+
+int
+srvpp_send_dumprequest(srvpp_ctx_t *ctx, void *request, void ***response_arr, size_t *response_cnt)
+{
+ vl_generic_request_t *req = NULL;
+ srvpp_request_ctx_t *req_ctx = NULL;
+ vl_api_control_ping_t *ping = NULL;
+ int retval = 0;
+
+ CHECK_NULL(ctx);
+ CHECK_NULL(request);
+ CHECK_NULL(response_arr);
+ CHECK_NULL(response_cnt);
+
+ req_ctx = srvpp_get_thread_request_ctx(ctx);
+ if (NULL == req_ctx) {
+ SRVPP_LOG_ERR_MSG("Unable to obtain a request context.");
+ return -1;
+ }
+
+ req = (vl_generic_request_t *) request;
+ req->client_index = ctx->vlib_client_index;
+
+ /* allocate control ping request */
+ ping = srvpp_alloc_msg(VL_API_CONTROL_PING, sizeof(*ping));
+ if (NULL == ping) {
+ SRVPP_LOG_ERR_MSG("Unable to allocate control ping message.");
+ return -1;
+ }
+ ping->client_index = ctx->vlib_client_index;
+
+ pthread_mutex_lock(&req_ctx->lock);
+
+ req_ctx->resp_data_copy = true;
+ req_ctx->resp_msg_cnt = 0;
+
+ req_ctx->resp_type = SRVPP_DETAILS;
+ req_ctx->resp_ready = false;
+ req_ctx->resp_retval = 0;
+
+ req_ctx->ctx_id++;
+ req->context = req_ctx->ctx_id;
+ ping->context = req_ctx->ctx_id;
+
+ pthread_mutex_unlock(&req_ctx->lock);
+
+ SRVPP_LOG_DBG_MSG("Sending a dumprequest to VPP.");
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&req);
+
+ vl_msg_api_send_shmem(ctx->vlib_input_queue, (u8*)&ping);
+
+ /* wait for expected response */
+ retval = srvpp_wait_response(req_ctx);
+
+ if (0 == retval) {
+ *response_arr = req_ctx->resp_msg_arr;
+ *response_cnt = req_ctx->resp_msg_cnt;
+ SRVPP_LOG_DBG_MSG("VPP dumprequest successfully processed.");
+ } else {
+ SRVPP_LOG_ERR_MSG("Error by handling of a VPP dumprequest.");
+ }
+
+ return retval;
+}
+
+int
+srvpp_get_if_index(srvpp_ctx_t *ctx, const char *if_name, uint32_t *if_index)
+{
+ CHECK_NULL(if_name);
+ CHECK_NULL(if_index);
+
+ srvpp_if_info_t *if_info = NULL;
+
+ if_info = ctx->if_info_list;
+
+ while ((NULL != if_info) && (NULL != if_info->if_name)) {
+ if (0 == strcmp(if_info->if_name, if_name)) {
+ *if_index = if_info->if_index;
+ return 0;
+ }
+ if_info = if_info->_next;
+ }
+
+ return 1;
+}
diff --git a/src/srvpp/src/srvpp.h b/src/srvpp/src/srvpp.h
new file mode 100644
index 0000000..4830d7b
--- /dev/null
+++ b/src/srvpp/src/srvpp.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#ifndef INC_SRVPP_H_
+#define INC_SRVPP_H_
+
+/**
+ * @defgroup srvpp Sysrepo-VPP Integration Library
+ * @{
+ *
+ * @brief Provides synchronous interface to VPP binary API aimed primarily for
+ * the integration of VPP with Sysrepo datastore.
+ *
+ * The library is thread-safe - can be used to communicate with VPP from multiple
+ * threads simultaneously.
+ */
+
+#include <stddef.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vapi/vpe.api.vapi.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+/**
+ * @brief Sysrepo - VPP interface context.
+ */
+typedef struct srvpp_ctx_s srvpp_ctx_t;
+
+/**
+ * @brief srvpp logger severity levels.
+ */
+typedef enum srvpp_log_level_e {
+ SRVPP_NONE, /**< Do not print any messages. */
+ SRVPP_ERR, /**< Print only error messages. */
+ SRVPP_WRN, /**< Print error and warning messages. */
+ SRVPP_INF, /**< Besides errors and warnings, print some other informational messages. */
+ SRVPP_DBG, /**< Print all messages including some development debug messages. */
+} srvpp_log_level_t;
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ *
+ * @param[in] level Severity level of the log entry.
+ * @param[in] message Message of the log entry.
+ */
+typedef void (*srvpp_log_cb)(srvpp_log_level_t level, const char *message);
+
+/**
+ * @brief Returns a reference to the global srvpp context. If the global context
+ * does not exists yet, it will be automatically created (a connection to VPP
+ * will be established).
+ *
+ * @note The caller is supposed to call ::srvpp_release_ctx after it finishes
+ * the work with VPP.
+ *
+ * @return srvpp context to be used for subsequent API calls.
+ */
+srvpp_ctx_t* srvpp_get_ctx();
+
+/**
+ * @brief Releases a reference to the global srvpp context. If this is the last
+ * reference, the context will be released (the connection to the VPP will be closed).
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ */
+void srvpp_release_ctx(srvpp_ctx_t *ctx);
+
+/**
+ * @brief Allocates a VPP API message of specified type and size.
+ *
+ * @param[in] msg_id Message ID.
+ * @param[in] msg_size Size of the message.
+ *
+ * @return Space allocated for the message.
+ */
+void* srvpp_alloc_msg(uint16_t msg_id, size_t msg_size);
+
+/**
+ * @brief Sends a simple request to VPP and receive the response.
+ *
+ * @note Subsequent ::srvpp_send_request or ::srvpp_send_dumprequest calls from the same
+ * thread may overwrite the content of ::response, therefore it is not safe to call
+ * them until the response is fully consumed / processed by the caller.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] request Message with the request to be sent to VPP.
+ * @param[out] response (optional) Response to the request received from VPP.
+ * Caller must not free it - to save memory allocations, response is always
+ * placed into the same thread-local buffer owned by the library.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_send_request(srvpp_ctx_t *ctx, void *request, void **response);
+
+/**
+ * @brief Sends a dump request to VPP and receives all responses with details.
+ *
+ * @note Subsequent ::srvpp_send_request or ::srvpp_send_dumprequest calls from the same
+ * thread may overwrite the content of ::response_arr, therefore it is not safe to call
+ * them until the response array is fully consumed / processed by the caller.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] request Message with the request to be sent to VPP.
+ * @param[out] response_arr Array of responses to the request received from VPP.
+ * Caller must not free it - to save memory allocations, response_arr is always
+ * placed into the same thread-local buffer owned by the library.
+ * @param[out] response_cnt Count of the response messages in the response_arr array.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_send_dumprequest(srvpp_ctx_t *ctx, void *request, void ***response_arr, size_t *response_cnt);
+
+/**
+ * @brief Get interface index for provided interface name.
+ *
+ * @param[in] ctx srvpp context acquired using ::srvpp_get_ctx call.
+ * @param[in] if_name Name of an existing VPP interface.
+ * @param[out] if_index Index of the interface.
+ *
+ * @return 0 on success, non-zero in case of error.
+ */
+int srvpp_get_if_index(srvpp_ctx_t *ctx, const char *if_name, uint32_t *if_index);
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ * Callback will be called for each message with any log level.
+ *
+ * @param[in] log_callback Callback to be called when a log entry would populated.
+ */
+void srvpp_set_log_cb(srvpp_log_cb log_callback);
+
+/**
+ * @brief Setups message handler to provided VPP API call. Needs to be called
+ * for each VPP API function that can arrive as response from VPP.
+ *
+ * @param[in] ID VPP API function ID.
+ * @param[in] NAME VPP API function name.
+ */
+#define srvpp_setup_handler(ID, NAME) \
+ do { \
+ vl_msg_api_set_handlers(VL_API_##ID, #NAME, \
+ (void*)_srvpp_receive_msg_handler, \
+ (void*)vl_noop_handler, \
+ (void*)vl_api_##NAME##_t_endian, \
+ (void*)vl_api_##NAME##_t_print, \
+ sizeof(vl_api_##NAME##_t), 1); \
+ } while(0)
+
+/**
+ * @brief Internal callback automatically called by VPP library when a message
+ * from VPP is received.
+ */
+void _srvpp_receive_msg_handler(void *message);
+
+/**@} srvpp */
+
+#endif /* INC_SRVPP_H_ */
diff --git a/src/srvpp/src/srvpp_logger.c b/src/srvpp/src/srvpp_logger.c
new file mode 100644
index 0000000..ee03a4b
--- /dev/null
+++ b/src/srvpp/src/srvpp_logger.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 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 <stdlib.h>
+#include <pthread.h>
+
+#include "srvpp_logger.h"
+
+#define MAX_LOG_MSG_SIZE 2048 /**< Maximum size of one log entry. */
+
+volatile srvpp_log_cb srvpp_log_callback = NULL; /**< Global variable used to store logging callback, if set. */
+
+static pthread_once_t srvpp_log_buff_create_key_once = PTHREAD_ONCE_INIT; /** Used to control that ::srvpp_log_buff_create_key is called only once per thread. */
+static pthread_key_t srvpp_log_buff_key; /**< Key for thread-specific buffer data. */
+
+/**
+ * @brief Create key for thread-specific buffer data. Should be called only once per thread.
+ */
+static void
+srvpp_log_buff_create_key(void)
+{
+ while (pthread_key_create(&srvpp_log_buff_key, free) == EAGAIN);
+ pthread_setspecific(srvpp_log_buff_key, NULL);
+}
+
+void
+srvpp_logger_init()
+{
+ /* no init needed */
+}
+
+void
+srvpp_logger_cleanup()
+{
+ /* since the thread-specific data for the main thread seems to be not auto-freed,
+ * (at least on Linux), we explicitly release it here for the thread from which
+ * sr_logger_cleanup has been called (which should be always the main thread). */
+ pthread_once(&srvpp_log_buff_create_key_once, srvpp_log_buff_create_key);
+ char *msg_buff = pthread_getspecific(srvpp_log_buff_key);
+ if (NULL != msg_buff) {
+ free(msg_buff);
+ pthread_setspecific(srvpp_log_buff_key, NULL);
+ }
+}
+
+void
+srvpp_log_to_cb(srvpp_log_level_t level, const char *format, ...)
+{
+ char *msg_buff = NULL;
+ va_list arg_list;
+
+ if (NULL != srvpp_log_callback) {
+ /* get thread-local message buffer */
+ pthread_once(&srvpp_log_buff_create_key_once, srvpp_log_buff_create_key);
+ msg_buff = pthread_getspecific(srvpp_log_buff_key);
+ if (NULL == msg_buff) {
+ msg_buff = calloc(MAX_LOG_MSG_SIZE, sizeof(*msg_buff));
+ pthread_setspecific(srvpp_log_buff_key, msg_buff);
+ }
+ /* print the message into buffer and call callback */
+ if (NULL != msg_buff) {
+ va_start(arg_list, format);
+ vsnprintf(msg_buff, MAX_LOG_MSG_SIZE - 1, format, arg_list);
+ va_end(arg_list);
+ msg_buff[MAX_LOG_MSG_SIZE - 1] = '\0';
+ /* call the callback */
+ srvpp_log_callback(level, msg_buff);
+ }
+ }
+}
+
diff --git a/src/srvpp/src/srvpp_logger.h b/src/srvpp/src/srvpp_logger.h
new file mode 100644
index 0000000..3a1c947
--- /dev/null
+++ b/src/srvpp/src/srvpp_logger.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#ifndef SRC_SRVPP_LOGGER_H_
+#define SRC_SRVPP_LOGGER_H_
+
+#include "srvpp.h"
+
+/**
+ * @brief Pointer to logging callback, if set.
+ */
+extern volatile srvpp_log_cb srvpp_log_callback;
+
+/**
+ * @brief Returns the string representing the specified log level.
+ */
+#define _SRVPP_LL_STR(LL) \
+ ((SRVPP_ERR == LL) ? "ERR" : \
+ (SRVPP_WRN == LL) ? "WRN" : \
+ (SRVPP_INF == LL) ? "INF" : \
+ (SRVPP_DBG == LL) ? "DBG" : \
+ "")
+
+/**
+ * @brief Internal logging macro for formatted messages.
+ */
+#ifdef NDEBUG
+#define _SRVPP_LOG_FMT(LL, MSG, ...) \
+ do { \
+ if (NULL != srvpp_log_callback) { \
+ srvpp_log_to_cb(LL, MSG, __VA_ARGS__); \
+ } else { \
+ fprintf(stderr, "[%s] " MSG "\n", _SRVPP_LL_STR(LL), __VA_ARGS__); \
+ } \
+} while(0)
+#else
+#define _SRVPP_LOG_FMT(LL, MSG, ...) \
+ do { \
+ if (NULL != srvpp_log_callback) { \
+ srvpp_log_to_cb(LL, "(%s:%d) " MSG, __func__, __LINE__, __VA_ARGS__); \
+ } else { \
+ fprintf(stderr, "[%s] (%s:%d) " MSG "\n", _SRVPP_LL_STR(LL), __func__, __LINE__, __VA_ARGS__); \
+ } \
+} while(0)
+#endif
+
+/* Logging macros for formatted messages. */
+#define SRVPP_LOG_ERR(MSG, ...) _SRVPP_LOG_FMT(SRVPP_ERR, MSG, __VA_ARGS__);
+#define SRVPP_LOG_WRN(MSG, ...) _SRVPP_LOG_FMT(SRVPP_WRN, MSG, __VA_ARGS__);
+#define SRVPP_LOG_INF(MSG, ...) _SRVPP_LOG_FMT(SRVPP_INF, MSG, __VA_ARGS__);
+#define SRVPP_LOG_DBG(MSG, ...) _SRVPP_LOG_FMT(SRVPP_DBG, MSG, __VA_ARGS__);
+
+/* Logging macros for unformatted messages. */
+#define SRVPP_LOG_ERR_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_ERR, MSG "%s", "");
+#define SRVPP_LOG_WRN_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_WRN, MSG "%s", "");
+#define SRVPP_LOG_INF_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_INF, MSG "%s", "");
+#define SRVPP_LOG_DBG_MSG(MSG) _SRVPP_LOG_FMT(SRVPP_DBG, MSG "%s", "");
+
+/**
+ * @brief Initializes logger.
+ */
+void srvpp_logger_init();
+
+/**
+ * @brief Cleans up resources used by he logger.
+ */
+void srvpp_logger_cleanup();
+
+/**
+ * @brief Logs into the callback pre-specified by ::srvpp_set_log_cb.
+ *
+ * @param[in] level Log level.
+ * @param[in] format Format message.
+ */
+void srvpp_log_to_cb(srvpp_log_level_t level, const char *format, ...);
+
+#endif /* SRC_SRVPP_LOGGER_H_ */
diff --git a/src/srvpp/tests/CMakeLists.txt b/src/srvpp/tests/CMakeLists.txt
new file mode 100644
index 0000000..37b8c32
--- /dev/null
+++ b/src/srvpp/tests/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2016 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_directories ("${PROJECT_SOURCE_DIR}/src")
+
+# check whether valgrind is installed
+find_program(valgrind_FOUND valgrind)
+
+# macro for adding of an unit test
+macro(ADD_UNIT_TEST TEST_NAME)
+ set(TEST_SRC
+ ${TEST_NAME}.c
+ )
+ add_executable(${TEST_NAME} ${TEST_SRC})
+ target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARIES} srvpp_a)
+ add_test(${TEST_NAME} ${TEST_NAME})
+
+ if(valgrind_FOUND)
+ add_test(${TEST_NAME}_valgrind valgrind
+ --error-exitcode=1 --read-var-info=yes
+ --leak-check=full --show-leak-kinds=all
+ ./${TEST_NAME}
+ )
+ endif(valgrind_FOUND)
+endmacro(ADD_UNIT_TEST)
+
+# add individual unit-tests
+ADD_UNIT_TEST(srvpp_test) \ No newline at end of file
diff --git a/src/srvpp/tests/srvpp_test.c b/src/srvpp/tests/srvpp_test.c
new file mode 100644
index 0000000..89e56db
--- /dev/null
+++ b/src/srvpp/tests/srvpp_test.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2016 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "srvpp.h"
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+static int
+srvpp_test_setup(void **state)
+{
+ srvpp_ctx_t *ctx = NULL;
+
+ ctx = srvpp_get_ctx();
+ assert_non_null(ctx);
+
+ srvpp_setup_handler(CONTROL_PING_REPLY, control_ping_reply);
+ srvpp_setup_handler(CREATE_LOOPBACK_REPLY, create_loopback_reply);
+ srvpp_setup_handler(DELETE_LOOPBACK_REPLY, delete_loopback_reply);
+ srvpp_setup_handler(SW_INTERFACE_DETAILS, sw_interface_details);
+
+ *state = ctx;
+ return 0;
+}
+
+static int
+srvpp_test_teardown(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+
+ srvpp_release_ctx(ctx);
+
+ return 0;
+}
+
+static void
+srvpp_ctx_test(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+ assert_non_null(ctx);
+
+ srvpp_ctx_t *ctx1 = NULL, *ctx2 = NULL;
+ vl_api_control_ping_t *msg = NULL;
+ int ret = 0;
+
+ /* try to get additional contexts */
+ ctx1 = srvpp_get_ctx();
+ assert_non_null(ctx1);
+
+ ctx2 = srvpp_get_ctx();
+ assert_non_null(ctx2);
+
+ assert_true(ctx == ctx1);
+ assert_true(ctx1 == ctx2);
+
+ /* send a control ping */
+ msg = srvpp_alloc_msg(VL_API_CONTROL_PING, sizeof(*msg));
+ assert_non_null(msg);
+
+ ret = srvpp_send_request(ctx1, msg, NULL);
+ assert_int_equal(ret, 0);
+
+ /* release not needed contexts */
+ srvpp_release_ctx(ctx1);
+ srvpp_release_ctx(ctx2);
+}
+
+static void
+srvpp_msg_test(void **state)
+{
+ srvpp_ctx_t *ctx = *state;
+ assert_non_null(ctx);
+
+ vl_api_create_loopback_t *create_loop_req = NULL;
+ vl_api_create_loopback_reply_t *create_loop_reply = NULL;
+ vl_api_delete_loopback_t *del_loop_req = NULL;
+ vl_api_sw_interface_dump_t *if_dump_req = NULL;
+ vl_api_sw_interface_details_t *if_details = NULL;
+ void **details = NULL;
+ size_t details_cnt = 0;
+ u8 mac[6] = { 0, 10, 11, 12, 13, 14};
+ int if_index = -1;
+ int ret = 0;
+
+ /* create a loopback interface */
+ create_loop_req = srvpp_alloc_msg(VL_API_CREATE_LOOPBACK, sizeof(*create_loop_req));
+ assert_non_null(create_loop_req);
+
+ memcpy(create_loop_req->mac_address, mac, sizeof(mac));
+
+ ret = srvpp_send_request(ctx, create_loop_req, (void**)&create_loop_reply);
+ assert_int_equal(ret, 0);
+ assert_non_null(create_loop_reply);
+ if_index = ntohl(create_loop_reply->sw_if_index);
+
+ printf("connected loopback interface, ifindex=%d\n", if_index);
+
+ /* dump interfaces */
+ if_dump_req = srvpp_alloc_msg(VL_API_SW_INTERFACE_DUMP, sizeof(*if_dump_req));
+ assert_non_null(if_dump_req);
+
+ ret = srvpp_send_dumprequest(ctx, if_dump_req, &details, &details_cnt);
+ assert_int_equal(ret, 0);
+
+ for (size_t i = 0; i < details_cnt; i++) {
+ if_details = (vl_api_sw_interface_details_t *) details[i];
+ printf("interface %s id=%d %s\n", if_details->interface_name, ntohl(if_details->sw_if_index), if_details->admin_up_down ? "up" : "down");
+ }
+
+ /* delete a loopback interface */
+ del_loop_req = srvpp_alloc_msg(VL_API_DELETE_LOOPBACK, sizeof(*del_loop_req));
+ assert_non_null(del_loop_req);
+
+ del_loop_req->sw_if_index = htonl(if_index);
+
+ ret = srvpp_send_request(ctx, del_loop_req, NULL);
+ assert_int_equal(ret, 0);
+}
+
+int
+main()
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(srvpp_ctx_test, srvpp_test_setup, srvpp_test_teardown),
+ cmocka_unit_test_setup_teardown(srvpp_msg_test, srvpp_test_setup, srvpp_test_teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}